Lecture11-切面-AOP相关概念

文章目录

    [toc]

    img019

    Lecture11-切面-AOP相关概念

    1、AOP概念介绍

    ①名词解释

    AOP:Aspect Oriented Programming 面向切面编程


    ②AOP的作用

    下面两点是同一件事的两面,一枚硬币的两面:

    • 简化代码:把方法中固定位置的重复的代码抽取出来,让被抽取的方法更专注于自己的核心功能,提高内聚性。
    • 代码增强:把特定的功能封装到切面类中,看哪里有需要,就往上套,被套用了切面逻辑的方法就被切面给增强了。

    2、横切关注点

    从每个方法中抽取出来的同一类非核心业务。在同一个项目中,我们可以使用多个横切关注点对相关方法进行多个不同方面的增强。

    这个概念不是语法层面天然存在的,而是根据附加功能的逻辑上的需要:有十个附加功能,就有十个横切关注点。

    ./images


    3、通知

    每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。

    • 前置通知:在被代理的目标方法前执行
    • 返回通知:在被代理的目标方法成功结束后执行(寿终正寝)
    • 异常通知:在被代理的目标方法异常结束后执行(死于非命)
    • 后置通知:在被代理的目标方法最终结束后执行(盖棺定论)
    • 环绕通知:使用try...catch...finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置

    ./images


    4、切面

    封装通知方法的类

    ./images


    5、目标

    被代理的目标对象。


    6、代理

    向目标对象应用通知之后创建的代理对象。


    7、连接点

    这也是一个纯逻辑概念,不是语法定义的。

    把方法排成一排,每一个横切位置看成x轴方向,把方法从上到下执行的顺序看成y轴,x轴和y轴的交叉点就是连接点。

    ./images


    8、切入点

    定位连接点的方式。

    每个类的方法中都包含多个连接点,所以连接点是类中客观存在的事物(从逻辑上来说)。

    如果把连接点看作数据库中的记录,那么切入点就是查询记录的 SQL 语句。

    Spring 的 AOP 技术可以通过切入点定位到特定的连接点。

    切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件


    9、基于注解的AOP用到的技术[参考资料]

    在Spring AOP中,代理创建的机制确实与版本和配置有关。你观察到的现象可能与以下几个因素有关:

    ①代理机制的历史变化

    • Spring 5及以前:默认策略是:
      • 有接口实现:使用JDK动态代理(Proxy.newProxyInstance)
      • 无接口:使用CGLIB代理

    ./images

    • 动态代理(InvocationHandler):JDK原生的实现方式,需要被代理的目标类必须实现接口。因为这个技术要求代理对象和目标对象实现同样的接口(兄弟两个拜把子模式)。
    • cglib:通过继承被代理的目标类(认干爹模式)实现代理,所以不需要目标类实现接口。
    • AspectJ:本质上是静态代理,将代理逻辑“织入”被代理的目标类编译得到的字节码文件,所以最终效果是动态的。weaver就是织入器。Spring只是借用了AspectJ中的注解。

    • Spring 6:默认使用强制CGLIB代理,无论目标类是否实现接口。这一变化在Spring 6.0的官方文档中有明确说明:

      "Since Spring Framework 6.0, the proxy-target-class attribute defaults to true, meaning that CGLIB proxies are used by default even for classes that implement interfaces."

    ②强制CGLIB代理的配置方式

    在Spring 6中,以下配置会强制使用CGLIB代理:

    @Configuration
    @EnableAspectJAutoProxy(proxyTargetClass = true)
    public class AppConfig {
        // 配置类内容
    }
    

    注意,从Spring 6开始,proxyTargetClass的默认值已经是true,所以无需显式设置。

    ③如何切换回JDK动态代理(如果需要)

    如果你希望恢复到旧的行为(有接口时使用JDK代理),可以显式配置:

    @Configuration
    @EnableAspectJAutoProxy(proxyTargetClass = false) // 强制使用JDK代理(有接口时)
    public class AppConfig {
        // 配置类内容
    }
    

    ④两种代理方式的区别

    JDK动态代理CGLIB代理
    基于接口实现基于类继承(通过字节码生成子类)
    只能代理实现了接口的类可以代理任何类(除final类/final方法)
    性能略高(直接调用接口方法)性能稍低(通过子类调用)

    ⑤为何Spring 6默认使用CGLIB?

    • 简化代理策略:统一使用CGLIB,避免开发者因接口存在与否而产生困惑。
    • 功能完整性:CGLIB支持代理类的所有方法,而JDK代理只能代理接口中定义的方法。
    • 现代应用趋势:越来越多的Spring应用使用@Component等注解,类通常不实现特定接口。

    ⑥总结

    如果你在Spring 6中发现AOP始终使用CGLIB,这是默认行为,而非配置错误。如果需要恢复到旧的JDK代理策略,可以通过@EnableAspectJAutoProxy(proxyTargetClass = false)显式配置。这种变化不会影响大多数应用,但在需要细粒度控制代理行为的场景下需要注意。

    end