Spring AOP

1.AOP概述

        什么是AOP?

        Spring 俩大核心: Spring IoC 和 Spring AOP

        IoC 控制反转(把Bean的控制权交给Spring来进行管理)

        AOP(Aspect Oriented Programming)面向切面编程.它和面向对象编程不是互斥关系,而是面向对象编程的补充.

        什么是⾯向切⾯编程呢?

        切⾯就是指某⼀类特定问题, 所以AOP也可以理解为面向特定方法编程.(也就是对于统一问题的解决方法)

        什么是⾯向特定⽅法编程呢? 比如"登录校验", 就是⼀类特定问题. 登录校验拦截器, 就是对"登录校验"这类问题的统⼀处理. 所以, 拦截器也是AOP的⼀种应⽤. AOP是⼀种思想, 拦截器是AOP思想的⼀种实现. Spring框架实现了这种思想, 提供了拦截器技术的相关接⼝. 同样的, 统⼀数据返回格式和统⼀异常处理, 也是AOP思想的⼀种实现.Spring 对AOP的实现就是Spring AOP.

        简单来说: AOP是⼀种思想, 是对某⼀类事情的集中处理.(用户的登录校验问题,结果的统一返回,异常的统一处理都是AOP思想的实现)

        什么是Spring AOP?

        AOP是⼀种思想, 它的实现⽅法有很多, 有Spring AOP,也有AspectJ、CGLIB等.

        Spring AOP是其中的⼀种实现⽅式.

        学会了统⼀功能之后, 是不是就学会了Spring AOP呢, 当然不是.

        拦截器作⽤的范围是所有的URL(⼀次请求和响应), @ControllerAdvice 应⽤场景主要是全局异常处理(配合⾃定义异常效果更佳), 数据绑定, 数据预处理. AOP作⽤的维度更加细致(可以根据包、类、⽅法名、参数等进⾏拦截), 能够实现更加复杂的业务逻辑.(每个方法,接口执行的时间,我们需要计算,因此我们需要对每个方法进行监控)

        比如我们在之前的图书管理系统的查询接口上测试耗时

        现在有⼀些业务的执⾏效率⽐较低, 耗时较⻓, 我们需要对接⼝进⾏优化.

        第⼀步就需要定位出执⾏耗时⽐较⻓的业务⽅法, 再针对该业务⽅法来进⾏优化

        如何定位呢? 我们就需要统计当前项⽬中每⼀个业务⽅法的执⾏耗时.

        如何统计呢? 可以在业务⽅法运⾏前和运⾏后, 记录下⽅法的开始时间和结束时间, 两者之差就是这个⽅法的耗时.        这种⽅法是可以解决问题的, 但⼀个项⽬中会包含很多业务模块, 每个业务模块⼜有很多接⼝, ⼀个接⼝⼜包含很多⽅法, 如果我们要在每个业务⽅法中都记录⽅法的耗时, 对于程序员而言, 会增加很多的⼯作量.

        AOP就可以做到在不改动这些原始方法的基础上, 针对特定的方法进行功能的增强.

        AOP的作用:在程序期间运行在不修改源代码的基础上对已有方法进行增强(⽆侵⼊性: 解耦)        

        举个例⼦:

        我们现在有⼀个项⽬, 项⽬中开发了很多的业务功能

        打印每个方法的时间戳就是一类问题,我们就提出一个问题: 是否可以使用AOP的方式把方法的耗时打印出来?

        现在我们要使用AOP来打印所有的接口耗时.我们来看具体实现

2. Spring AOP快速入门
        

        2.1 引入依赖,在pom文件引入依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>

        2.2 写一个切面(一类特定的问题)

        @Aspect和AspectJ的关系

        我们自己写一个切面并且进行分析: 我们只是写了一个类,但是却影响了整个程序,只要调用接口就会把耗时给打印出来,对其他类的侵入性小.

        

        注意 @Around("execution(* org.xiaobai.library.controller.*.*(..))"),如果我们只对BookController生效就要写成@Around("execution(* org.xiaobai.library.controller.BookController.*(..))"),如果要对不止controller里面的代码生效的话: @Around("execution(* org.xiaobai.library.*.*.*(..))")

        然后我们对刚刚的代码进行分析

        1. @Aspect: 标识这是⼀个切⾯类

        2. @Around: 环绕通知, 在⽬标⽅法的前后都会被执⾏. 后⾯的表达式表⽰对哪些⽅法进⾏增强.

        3. ProceedingJoinPoint.proceed() 让原始⽅法执⾏

        AOP的优势(面试)

        • 代码⽆侵⼊: 不修改原始的业务⽅法, 就可以对原始的业务⽅法进⾏了功能的增强或者是功能的改变

        • 减少了重复代码

        • 提⾼开发效率

        • 维护方便

3. Spring AOP 详细解释

        下⾯我们再来详细学习AOP, 主要是以下⼏部分

        • Spring AOP中涉及的核⼼概念

        • Spring AOP通知类型

        • 多个AOP程序的执⾏顺序

        3.1 Spring AOP核心概念

                切点

        切点(Pointcut), 也称之为"切⼊点"

        Pointcut 的作⽤就是提供⼀组规则 (使⽤ AspectJ pointcut expression language (切点表达式)来描述), 告诉程序对哪些方法来进行功能增强(对哪些方法生效).比如我们刚刚的"execution(* org.xiaobai.library.controller.*.*(..))"

                连接点

        满⾜切点表达式规则的方法, 就是连接点. 也就是可以被AOP控制的方法.(具体作用的方法,也就是我们的切点对哪些方法进行了功能增强,那么这些方法就是连接点

         比如我们BookController里面所有的方法,都满足上述的表达式

        切点和连接点的关系

        连接点是满⾜切点表达式的元素. 切点可以看做是保存了众多连接点的⼀个集合. 比如:

        切点表达式: 全体教师

        连接点就是: 张三,李四等各个⽼师

                通知

        通知就是具体要做的⼯作, 指哪些重复的逻辑,也就是共性功能(最终体现为⼀个⽅法) 比如上述程序中记录业务⽅法的耗时时间, 就是通知.(连接点具体要做的事情).

        在AOP⾯向切⾯编程当中, 我们把这部分重复的代码逻辑抽取出来单独定义, 这部分代码就是通知的内容.

                切面

        切⾯(Aspect) = 切点(Pointcut) + 通知(Advice)

        通过切⾯就能够描述当前AOP程序需要针对于哪些方法, 在什么时候执⾏什么样的操作.

        切⾯既包含了通知逻辑的定义, 也包括了连接点的定义.

        切⾯所在的类, 我们⼀般称为切⾯类(被@Aspect注解标识的类),切面类里面可以有很多个切面


        切点,切面,通知的关系

        切点是一组规则,主要是规定参与的范围是什么,连接点是参与范围的每一个个体,通知是具体做什么,切面是整个事情.

        3.2 通知类型 

        上⾯我们讲了什么是通知(具体要做的事情,但是我们要做发事情可以分为在目标方法执行前做的,也可以是在目标方法执行后做的), 接下来学习通知的类型(哪一个时机执行的). @Around 就是其中⼀种通知类型, 表示环绕通知. Spring中AOP的通知类型有以下⼏种:

        • @Around: 环绕通知, 此注解标注的通知⽅法(也就是我们写的通知)在⽬标⽅法前, 后都被执⾏

        • @Before: 前置通知, 此注解标注的通知⽅法在⽬标⽅法前被执⾏

        • @After: 后置通知, 此注解标注的通知⽅法在⽬标⽅法后被执⾏, ⽆论是否有异常都会执⾏

        • @AfterReturning: 返回后通知, 此注解标注的通知⽅法在⽬标⽅法后被执⾏, 有异常不会执⾏

        • @AfterThrowing: 异常后通知, 此注解标注的通知⽅法发⽣异常后执⾏

        注意: @Around的返回值必须要有返回结果不然controller不会生效,因为我们执行的是目标函数,需要把结果返回

总体的代码例子

程序正常运⾏的情况下, @AfterThrowing 标识的通知⽅法不会执⾏

从上图也可以看出来, @Around 标识的通知⽅法包含两部分, ⼀个"前置逻辑", ⼀个"后置逻辑".其中"前置逻辑" 会先于 @Before 标识的通知⽅法执⾏, "后置逻辑" 会晚于 @After 标识的通知⽅法执⾏.

程序发⽣异常的情况下:

• @AfterReturning 标识的通知⽅法不会执⾏, @AfterThrowing 标识的通知⽅法执⾏了

• @Around 环绕通知中原始⽅法调⽤时有异常,通知中的环绕后的代码逻辑也不会在执⾏了(因为

原始⽅法调⽤出异常了)

 

        3.3 @PointCut

        上⾯代码存在⼀个问题, 就是存在⼤量重复的切点表达式 execution(* com.example.demo.controller.*.*(..)) ,如果我们要修改表达式,那么就会要修改很多地方, Spring提供了 @PointCut 注解, 把公共的切点表达式提取出来, 需要⽤到时引⽤该切⼊点表达式即可.

        @PointCut表示声明一个切点,它所修饰的方法名称就是切点的名称


        注意: 当切点定义使⽤private修饰时, 仅能在当前切⾯类中使⽤, 当其他切⾯类也要使⽤当前切点定义时, 就需要把private改为public. 引用为: 全限定类名.方法名(). (切点跨类使用)

         如果有多个切面的话,程序的执行顺序,默认按照名称进行

        AspectDemo2,AspectDemo3,AspectDemo4,数字越大优先级越低

        3.4 切面优先级 @Order

        当我们在⼀个项⽬中, 定义了多个切⾯类时, 并且这些切⾯类的多个切⼊点都匹配到了同⼀个⽬标⽅法. 当⽬标⽅法运⾏的时候, 这些切⾯类中的通知⽅法都会执⾏, 那么这⼏个通知⽅法的执⾏顺序是什么样的呢?

        存在多个切⾯类时, 默认按照切⾯类的类名字⺟排序:

        • @Before 通知:字⺟排名靠前的先执⾏

        • @After 通知:字⺟排名靠前的后执⾏

        但这种⽅式不⽅便管理, 我们的类名更多还是具备⼀定含义的.

        Spring 给我们提供了⼀个新的注解, 来控制这些切⾯通知的执⾏顺序,: @Order 使⽤⽅式如下:

        Order里面是优先级的大小,数字越大,优先级越低

        3.5 切点表达式

上⾯的代码中, 我们⼀直在使⽤切点表达式来描述切点. 下⾯我们来介绍⼀下切点表达式的语法. 切点表达式常⻅有两种表达⽅式

1. execution(RR):根据⽅法的签名来匹配

2. @annotation(RR) :根据注解匹配

        3.5.1 execution表达式 

        

        execution() 是最常⽤的切点表达式, ⽤来匹配⽅法, 语法为:

        execution(<访问修饰符> <返回类型> <包名.类名.⽅法(⽅法参数)> <异常>)

        其中,访问修饰符和异常可以省略

        切点表达式⽀持通配符表达:

        *表示任意单词,可以提花返回类型,包名,类名方法等

        .. 表示任意多个单词

        更加详细的介绍

        切点表达式示例

        TestController 下的 public修饰, 返回类型为String ⽅法名为t1, 无参方法((完整写法)

         execution(public String com.example.demo.controller.TestController.t1())

        省略访问修饰符: 表示适应所有的访问修饰限定符,返回类型String,在controller包里面的TestController类里面的t1无参方法

            execution(String com.example.demo.controller.TestController.t1())

        匹配所有返回类型: 匹配在controller包里面的TestController类里面的t1无参方法,任意返回类型,适合所有访问修饰限定符的方法

                execution(* com.example.demo.controller.TestController.t1())

        匹配TestController 下的所有⽆参⽅法

                execution(* com.example.demo.controller.TestController.*())

        匹配TestController 下的所有⽅法

                 execution(* com.example.demo.controller.TestController.*(..))

       

         匹配controller包下所有的类的所有⽅法

                 execution(* com.example.demo.controller.*.*(..)) 

                如果controller下面还有很多包

                 execution(* com.example.demo.controller..*(..)) 

        匹配所有包下⾯的TestController

                 execution(* com..TestController.*(..))

        匹配com.example.demo包下, ⼦孙包下的所有类的所有⽅法

                 execution(* com.example.demo..*(..))

        匹配特定方法名且有特定参数的方法

                execution(* myMethod(String, int))

        

        匹配特定方法名且有特定参数,并且抛出异常的方法

                exectution(* myMethod(String,int) throws IOException) 

         上面的方法是适合于有规则可循的

                3.5.2 @annotation

        execution表达式更适⽤有规则的(统一的), 如果我们要匹配多个⽆规则的⽅法呢, ⽐如:TestController中的t1()和UserController中的u1()这两个⽅法.这个时候我们使⽤execution这种切点表达式来描述就不是很⽅便了.

        我们可以借助⾃定义注解的⽅式以及另⼀种切点表达式 @annotation(零散无规则) 来描述这⼀类的切点.

       execution表达式适合于自己写项目,组件和框架的开发适合annotation

        实现步骤:

        1. 编写⾃定义注解

        2. 使⽤ @annotation 表达式来描述切点

        3. 在连接点的⽅法上添加⾃定义注解

        自定义注解里面的元注解解释

        这种写法不止适用于自定义注解,我也可以规定是作用于哪个包里面的哪个类的哪个被spring的注解修饰的方法,假设我是想作用于所有使用RequestMapping注解的方法.我们发下

Spring AOP的实现方式(面试题) 

        1. 基于注解 @Aspect (参考上述内容)基于execution

        2. 基于⾃自定义注解 (参考⾃定义注解 @annotation 部分的内容)基于annotation

        3. 基于Spring API (通过xml配置的⽅式, 自从SpringBoot ⼴泛使⽤之后, 这种⽅法⼏乎看不到了)1> 配置切面类 2> 通过<aop: config>来配置方法

        4. 基于代理来实现(更加久远的⼀种实现⽅式, 写法笨重, 不建议使⽤)后续会聊代理模式

        参考: https://cloud.tencent.com/developer/article/2032268

        总体框架

4. Spring AOP 原理

        上⾯我们主要学习了Spring AOP的应⽤, 接下来我们来学习Spring AOP的原理, 也就是Spring 是如何实现AOP的.

        Spring AOP 是基于动态代理来实现AOP的, 咱们学习内容主要分以下两部分

        1. 代理模式

        2. Spring AOP源码剖析

        一般我们设计模式要回答: 思想,实现方式,应用场景(几个方面)

        4.1 代理模式

        代理模式, 也叫委托模式.(不同模式的实现方法是不同的,每个模式的实现方法也不止一种)

        定义:为其他对象(房东)提供⼀种代理(中介)以控制对这个对象的访问. 它的作⽤就是通过提供⼀个代理类, 让我们在调用目标方法的时候, 不再是直接对⽬标⽅法进⾏调⽤, 而是通过代理类间接调⽤.(有时候房东不能提供一些服务,比如我哪哪哪坏了,需要修理,此时代理的作用就出来了)

        在某些情况下, ⼀个对象不适合或者不能直接引⽤另⼀个对象, ⽽代理对象可以在客户端和⽬标对象之间起到中介的作⽤.

        使用场景

        1> 无法直接访问目标对象

        2> 目标对象给我们提供的功能不够,希望对目标对象进行功能上的增强

        使⽤代理前:

        使⽤代理后:

        代理模式的主要角色

        1. Subject: 业务接⼝类. 可以是抽象类或者接⼝(要求房东做的事情)

        2. RealSubject: 业务实现类. 具体的业务执⾏, 也就是被代理对象. (房东)

        3. Proxy: 代理类. RealSubject的代理.(中介)

        ⽐如房屋租赁

        Subject 就是提前定义了房东做的事情, 交给中介代理, 也是中介要做的事情

        RealSubject: 房东

        Proxy: 中介

        代理模式可以在不修改被代理对象的基础上, 通过扩展代理类, 进⾏⼀些功能的附加与增强. 根据代理的创建时期, 代理模式分为静态代理和动态代理.

        • 静态代理: 由程序员创建代理类或特定⼯具⾃动⽣成源代码再对其编译, 在程序运⾏前代理类的(在程序运行前代理就存在了) 

        .class ⽂件就已经存在了.

        • 动态代理: 在程序运⾏时, 运⽤反射机制动态创建而成

        我们任然用房东和中介的关系举例: 我们在豆瓣上看房,我们看上房子后,我们要约中介实地带看.

        中介存在俩种:

        1> 每个房子提前分配好中介了,房源A -> 中介A,房源B ->中介B

        2> 选择想看的房子之后,提交信息,平台给我分配中介

        从上面可知,中介产生的时机分为俩种:

        1> 提前分配(静态代理)

        2> 运行时分配(动态代理)

        4.1.1 静态代理

        静态代理: 在程序运⾏前, 代理类的 .class⽂件就已经存在了. (在出租房⼦之前, 中介已经做好了相关的⼯作, 就等租⼾来租房⼦了)

        我们通过代码来加深理解. 以房租租赁为例

        1. 定义接⼝(定义房东要做的事情, 也是中介需要做的事情)

        

        2. 实现接⼝(房东出租房⼦)

        3. 代理(中介, 帮房东出租房⼦)

        4. 使用

        
        代码演示

        上⾯这个代理实现⽅式就是静态代理(仿佛啥也没⼲).

        从上述程序可以看出, 虽然静态代理也完成了对⽬标对象的代理, 但是由于代码都写死了, 对⽬标对象的每个⽅法的增强都是⼿动完成的,⾮常不灵活. 所以⽇常开发⼏乎看不到静态代理的场景.

        接下来新增需求: 中介⼜新增了其他业务: 代理房屋出售 我们需要对上述代码进⾏修改

 

        从上述代码可以看出, 我们修改接⼝(Subject)和业务实现类(RealSubject)时, 还需要修改代理类(Proxy).

        同样的, 如果有新增接⼝(Subject)和业务实现类(RealSubject), 也需要对每⼀个业务实现类新增代理类(Proxy).

        既然代理的流程是⼀样的(可以进一波封装了), 有没有⼀种办法, 让他们通过⼀个代理类来实现呢? 这就需要⽤到动态代理技术了.

        4.1.2 动态代理

        相⽐于静态代理来说,动态代理更加灵活.

        我们不需要针对每个⽬标对象都单独创建⼀个代理对象, 而是把这个创建代理对象的⼯作推迟到程序运⾏时由JVM来实现. 也就是说动态代理在程序运⾏时, 根据需要动态创建⽣成.

        ⽐如房屋中介, 我不需要提前预测都有哪些业务, ⽽是业务来了我再根据情况创建.

我们还是先看代码再来理解.

        Java也对动态代理进行了实现, 并给我们提供了⼀些API, 常见的实现⽅式有两种:

        1. JDK动态代理

        2. CGLIB(也是SpringAOP的一种实现方式)动态代理 

        动态代理在我们⽇常开发中使⽤的相对较少,但是在框架中几乎是必用的一门技术. 学会了动态代理之后, 对于我们理解和学习各种框架的原理也⾮常有帮助

        JDK动态代理

        JDK 动态代理类实现步骤

        1. 定义⼀个接⼝及其实现类(静态代理中的 HouseSubject 和 RealHouseSubject )

        2. ⾃定义 InvocationHandler 并重写 invoke ⽅法,在 invoke ⽅法中我们会调⽤目标方法(被代理类的⽅法)并⾃定义⼀些处理逻辑(Spring基于动态代理实现的,动态代理底层是反射实现的)

        3. 通过Proxy.newProxyInstance(ClassLoader loader,Class<?>[]interfaces,InvocationHandler h)来创建代理对象

        实现 InvocationHandler 接⼝

        创建⼀个代理对象并使⽤

  代码讲解

        1. InvocationHandler

        InvocationHandler 接⼝是Java动态代理的关键接⼝之⼀, 它定义了⼀个单⼀⽅法 invoke() , ⽤于处理被代理对象的⽅法调⽤.

        通过实现 InvocationHandler 接⼝, 可以对被代理对象的⽅法进⾏功能增强.


        2. Proxy

        Proxy 类中使⽤频率最⾼的⽅法是: newProxyInstance() , 这个⽅法主要⽤来⽣成⼀个代理对象

        这个⽅法⼀共有 3 个参数:

         Loader: 类加载器, ⽤于加载代理对象.

        interfaces : 被代理类实现的⼀些接⼝(这个参数的定义, 也决定了JDK动态代理只能代理实现了接⼝的⼀些类)

        h : 实现了 InvocationHandler 接⼝的对象

        总的调用流程

        但是有个缺陷: JDK只能代理接口,不能代理类

        CGLIB动态代理

        JDK 动态代理有⼀个最致命的问题是其只能代理实现了接⼝的类.

        有些场景下, 我们的业务代码是直接实现的, 并没有接⼝定义. 为了解决这个问题, 我们可以⽤ CGLIB 动态代理机制来解决.

        CGLIB(Code Generation Library)是⼀个基于ASM的字节码⽣成库,它允许我们在运⾏时对字节码进⾏修改和动态⽣成. CGLIB 通过继承⽅式实现代理, 很多知名的开源框架都使⽤到了CGLIB. 例如 Spring 中的 AOP 模块中: 如果目标对象实现了接口,则默认采⽤ JDK 动态代理, 否则采⽤ CGLIB 动态代理.(它既可以代理类,也可以代理接口)

        添加依赖

        

<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>

        CGLIB 动态代理类实现步骤

         1. 定义⼀个类(被代理类)

        2. 自定义 MethodInterceptor 并重写 intercept ⽅法, intercept ⽤于增强⽬标⽅法,和 JDK 动态代理中的 invoke 方法类似

        3. 通过 Enhancer 类的 create()创建代理类

        ⾃定义 MethodInterceptor(⽅法拦截器) 实现MethodInterceptor接⼝

        创建代理类, 并使⽤

        代码简单讲解

         1. MethodInterceptor

        MethodInterceptor 和 JDK动态代理中的 InvocationHandler 类似, 它只定义了⼀个⽅法 intercept() , ⽤于增强⽬标⽅法.

        

        2. Enhancer.create()

        Enhancer.create() ⽤来⽣成⼀个代理对象

        

        参数说明:

        type: 被代理类的类型(类或接⼝)

        callback: ⾃定义⽅法拦截器 MethodInterceptor

        

        JDK和CGLib的区别(面试题)

        Spring Boot是基Spring进行开发的,因此Spring又基于proxyTargetClass 进行开发的

        因此俩者在不同设置上代理是不同的

        总的来说: JDK可以代理接口(实现了接口的类),不可以代理类(没有实现接口的普通类). CGLib可以代理接口,又可以代理类

        Sping 

        默认proxyTargetClass 是false

        实现了接口 使用 JDK代理

        普通类 使用CGLib代理

        Spring Boot 从2.x之后

        默认proxyTargetClass 是true

        默认使用的是CGLib代理

        可以参考这个图

    

        

        源码解释

        proxyTargetClass设置

        我们下面会对这个图进行具体的解释

        可以通过 @EnableAspectJAutoProxy(proxyTargetClass = true) 来设置

        注意:

Spring Boot 2.X开始, 默认使⽤CGLIB代理

可以通过配置项 spring.aop.proxy-target-class=false 来进⾏修改,设置默认为jdk代理 SpringBoot设置 @EnableAspectJAutoProxy ⽆效, 因为Spring Boot 默认使⽤ AopAutoConfiguration进⾏装配

        

        设置CGLib代理(默认未true)

        未实现接口

        普通类

        

        debug结果

        实现了接口

        实现了接口的类

        

        debug结果

        配置改为false

        如果是代理普通类

        任然还是CGLIib

        

        如果是代理实现了接口的类

        那么就强制转换为jdk代理

 刚刚演示的是这一块代码的逻辑

总结八股文

        1. 什么是AoP: 对于一类事情的解决..实现思想...实现方式(CGLib,JDK,Spring AOP..)

        2. Spring AOP 的实现方式

        1>通过注解

        @Aspect

                exevution,自定义注解

        2>基于XML的方式(俩种)

        3>基于代理

        3. Spring AOP的实现原理

                基于动态代理实现

                        1> JDK

                        2> CGLib

        4. Spring 使用的是那种代理方式?

        都使用了(看源码晓得的),在这里Spring和SpringBoot的实现有区别

        主要和参数proxyTargetClass有关系

        先解释这个图

        再解释Spring和SpringBoot

          Sping 

        默认proxyTargetClass 是false

        实现了接口 使用 JDK代理

        普通类 使用CGLib代理

        Spring Boot 从2.x之后

        默认proxyTargetClass 是true

        默认使用的是CGLib代理

        

        5. JDK 和 CGLib的区别

        其他总结

        1. AOP是⼀种思想, 是对某⼀类事情的集中处理. Spring框架实现了AOP, 称之为SpringAOP

        2. Spring AOP常⻅实现⽅式有两种: 1. 基于注解@Aspect来实现 2. 基于⾃定义注解来实现, 还有⼀些更原始的⽅式,⽐如基于代理, 基于xml配置的⽅式, 但⽬标⽐较少⻅

        3. Spring AOP 是基于动态代理实现的, 有两种⽅式: 1. 基本JDK动态代理实现 2. 基于CGLIB动态代理实现. 运⾏时使⽤哪种⽅式与项⽬配置(proxyTargetClass)和代理的对象(是否实现接口)有关.

        

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/22764.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【多模态处理篇二】【深度揭秘:DeepSeek视频理解之时空注意力机制解析】

一、为啥要搞视频理解这事儿 咱先唠唠为啥视频理解这么重要哈。现在这互联网时代,视频那可是铺天盖地的。你刷短视频平台,看在线电影,玩游戏直播,到处都是视频。但是计算机它一开始可不懂视频里到底是啥意思,它看到的就是一堆像素点和声音信号。 视频理解呢,就是要让计…

Linux基本指令(三)+ 权限

文章目录 基本指令grep打包和压缩zip/unzipLinux和windows压缩包互传tar&#xff08;重要&#xff09;Linux和Linux压缩包互传 bcuname -r常用的热键关机外壳程序 知识点打包和压缩 Linux中的权限用户权限 基本指令 grep 1. grep可以过滤文本行 done用于标记循环的结束&#x…

DPVS-1:编译安装DPVS (ubuntu22.04)

操作系统 rootubuntu22:~# lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04.3 LTS Release: 22.04 Codename: jammy rootubuntu22:~# 前置软件准备 apt install git apt install meson apt install gcc ap…

三、linux字符驱动详解

在上一节完成NFS开发环境的搭建后&#xff0c;本节将探讨Linux字符设备驱动的开发。字符设备驱动作为Linux内核的重要组成部分&#xff0c;主要负责管理与字符设备&#xff08;如串口、键盘等&#xff09;的交互&#xff0c;并为用户空间程序提供统一的读写操作接口。 驱动代码…

[SQL] 事务的四大特性(ACID)

&#x1f384;事务的四大特性 以下就是事务的四大特性&#xff0c;简称ACID。 原子性&#x1f4e2;事务时不可分割的最小操作单元&#xff0c;要么全部成功&#xff0c;要么全部失败。一致性&#x1f4e2;事务完成后&#xff0c;必须使所有的数据都保持一致隔离性&#x1f4e2…

如何使用3D高斯分布进行环境建模

使用3D高斯分布来实现建模&#xff0c;主要是通过高斯分布的概率特性来描述空间中每个点的几何位置和不确定性。具体来说&#xff0c;3D高斯分布被用来表示点云数据中的每一个点或体素&#xff08;voxel&#xff09;的空间分布和不确定性&#xff0c;而不是单纯地存储每个点的坐…

蓝桥杯笔记——递归递推

递归 0. 函数的概念 我们从基础讲起&#xff0c;先了解函数的概念&#xff0c;然后逐步引入递归&#xff0c;帮助同学们更好地理解递归的思想和实现方式。 函数是程序设计中的一个基本概念&#xff0c;简单来说&#xff0c;它是一段封装好的代码&#xff0c;可以在程序中多次…

C++ IDE设置 visual studio 2010安装、注册、使用

Visual Studio 2010 C学习版 系列教程_哔哩哔哩_bilibiliVisual Studio 2010 C学习版 系列教程共计16条视频&#xff0c;包括&#xff1a;Visual Studio C 2010学习版 安装教程、Visual Studio C 2010学习版 激活方法、Visual Studio C 2010学习版 软件使用教学等&#xff0c;U…

细说Java 引用(强、软、弱、虚)和 GC 流程(一)

一、引用概览 1.1 引用简介 JDK1.2中引入了 Reference 抽象类及其子类&#xff0c;来满足不同场景的 JVM 垃圾回收工作&#xff1a; SoftReference 内存不足&#xff0c;GC发生时&#xff0c;引用的对象&#xff08;没有强引用时&#xff09;会被清理&#xff1b;高速缓存使用…

win11系统无法打开软件_组策略无法打开_gpedit.msc不生效_为了对电脑进行保护,已经阻止此应用---Windows工作笔记057

碰到这个问题挺麻烦的,要用的软件打不开了. 其实解决方法就是去组策略中修改一个策略就可以了,但是: 先来说: 而且,使用cmd输入的gpedit.msc也打不开了. 这个怎么解决? @echo off pushd "%~dp0"dir /b C:\Windows\servicing\Packages\Microsoft-Windows-GroupPo…

算法日记23:SC16+17(求数的因子+质因子)

题目1&#xff1a; 求解因子 题解1&#xff1a; 1&#xff09;易得&#xff0c;当 n a ∗ b na*b na∗b时&#xff0c; a , b {a,b} a,b是n的因子(假设 a < b a<b a<b) 可以发现只需枚举到即可 n \sqrt{n} n ​&#xff0c;因为 a < n < b a<\sqrt{n}&l…

欢乐力扣:同构字符串

文章目录 1、题目描述2、 代码 1、题目描述 同构字符串。给定两个字符串 s 和 t &#xff0c;判断它们是否是同构的。如果 s 中的字符可以按某种映射关系替换得到 t &#xff0c;那么这两个字符串是同构的。  每个出现的字符都应当映射到另一个字符&#xff0c;同时不改变字符…

【HeadFirst系列之HeadFirst设计模式】第7天之命令模式:封装请求,轻松实现解耦!

命令模式&#xff1a;封装请求&#xff0c;轻松实现解耦&#xff01; 大家好&#xff01;今天我们来聊聊设计模式中的命令模式&#xff08;Command Pattern&#xff09;。如果你曾经需要将请求封装成对象&#xff0c;或者希望实现请求的撤销、重做等功能&#xff0c;那么命令模…

敏捷开发07:敏捷项目可视化管理-ScrumBoard(Scrum板)使用介绍

ScrumBoard(Scrum板)介绍 ScrumBoard&#xff08;Scrum板&#xff09;是敏捷项目管理中使用的可视化工具&#xff0c;用于跟踪和监控冲刺阶段的任务进度。 主要通过可视化的看板来管理工作&#xff0c;它可视化了敏捷开发中的工作流程、任务状态、团队角色。 Scrum 团队在各…

Linux第十三节 — 进程状态详解

只要一个进程的PCB还存在内存当中&#xff0c;哪怕此时该进程对应的代码和数据已经在磁盘当中&#xff0c;此时依然认为该进程仍然存在&#xff01; 一、Linux进程的运行状态R 接下来我们看下面这个例子&#xff1a; 当我们执行这个程序的时候&#xff0c;我们认为该进程的状…

无人机遥控器接口作用详解!

USB接口&#xff1a; 功能&#xff1a;USB接口是一种通用串行总线接口&#xff0c;用于连接外部设备&#xff0c;如手机、平板、电脑或充电设备。在无人机遥控器上&#xff0c;USB接口通常用于数据传输和充电。 应用&#xff1a;用户可以通过USB接口将遥控器与电脑连接&#…

SVN把英文换中文

原文链接&#xff1a;SVN设置成中文版本 都是英文&#xff0c;换中文 Tortoise SVN 安装汉化教程(乌龟SVN) https://pan.quark.cn/s/cb6f2eee3f90 下载中文包

云手机如何进行经纬度修改

云手机如何进行经纬度修改 云手机修改经纬度的方法因不同服务商和操作方式有所差异&#xff0c;以下是综合多个来源的常用方法及注意事项&#xff1a; 通过ADB命令注入GPS数据&#xff08;适用于技术用户&#xff09; 1.连接云手机 使用ADB工具连接云手机服务器&#xff0c;…

【微服务优化】ELK日志聚合与查询性能提升实战指南

网罗开发 &#xff08;小红书、快手、视频号同名&#xff09; 大家好&#xff0c;我是 展菲&#xff0c;目前在上市企业从事人工智能项目研发管理工作&#xff0c;平时热衷于分享各种编程领域的软硬技能知识以及前沿技术&#xff0c;包括iOS、前端、Harmony OS、Java、Python等…

transfmer学习认识

整体架构 1.自注意机制 1.1.softmax 在机器学习和深度学习中&#xff0c;softmax 函数是一个常用的激活函数&#xff0c;用于将一个向量转换为一个概率分布。softmax 函数的公式如下&#xff1a; ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/35c158988402498ba6…