Spring AOP 的实现原理是什么?

在这里插入图片描述

核心思想:动态代理 (Dynamic Proxy)

Spring AOP 的实现原理不是通过修改你编写的 Java 源代码或 .class 文件,而是通过在运行时动态地创建一个代理对象 (Proxy) 来实现的。

这个代理对象“包裹”了你的原始目标对象 (Target Object),并拦截对目标对象方法的调用。当外部代码调用这个代理对象的方法时,代理对象就会在调用真正的目标方法前后,插入你的切面逻辑(即通知 Advice)。

整个过程对调用方是透明的,调用方以为自己拿到的就是原始对象,但实际上它拿到的是功能被增强了的代理对象。


两种主要的实现方式

Spring AOP 根据你的目标对象是否实现了接口,会自动选择两种不同的动态代理技术:

  1. JDK 动态代理 (JDK Dynamic Proxy)
  2. CGLIB 代理 (Code Generation Library)
1. JDK 动态代理
  • 使用条件目标对象必须实现至少一个接口。

  • 实现原理

    • Spring AOP 使用 Java java.lang.reflect.Proxy 类和 InvocationHandler 接口来创建代理对象。
    • 这个代理对象会实现目标对象所实现的所有接口
    • 当你通过代理对象调用一个方法时,这个调用会被转发到 InvocationHandlerinvoke() 方法中。
    • invoke() 方法内部,Spring AOP 框架会根据切点(Pointcut)和通知(Advice)的配置,决定是执行前置通知、调用目标方法、执行后置通知,还是处理异常等。
  • 优点

    • 是 Java 原生技术,无需引入第三方库。
  • 缺点

    • 只能代理实现了接口的类。如果一个类没有实现任何接口,JDK 动态代理就无能为力。

流程图 (JDK 动态代理):

+----------------+      实现了       +---------------+
|  Target Object |<-----------------|  SomeInterface|
| (UserService)  |                  +---------------+
+----------------+                         ^
                                           | 实现了
                                           |
+-------------------------------------------------+
|   Proxy Object (由 Spring 在运行时创建)         |
| - 实现了 SomeInterface                        |
| - 内部持有一个 InvocationHandler (包含切面逻辑)  |
| - 内部持有一个对 Target Object 的引用           |
+-------------------------------------------------+
2. CGLIB 代理
  • 使用条件目标对象没有实现任何接口(或者你强制配置使用 CGLIB)。

  • 实现原理

    • CGLIB 是一个强大的第三方代码生成库。它通过字节码增强技术 (Bytecode Enhancement) 来工作。
    • 它会在运行时动态地创建一个目标对象的子类作为代理对象。
    • 然后,它会重写 (Override) 目标对象中所有非 final 的方法
    • 在这些被重写的子类方法中,它会插入你的切面逻辑(类似于 JDK 代理中的 invoke 方法回调),然后再通过 super.method() 调用真正的父类(即目标对象)的业务逻辑。
  • 优点

    • 可以代理没有实现接口的类,适用范围更广。
    • 在某些场景下,性能可能比 JDK 代理略好(因为避免了反射)。
  • 缺点

    • 无法代理 final 方法或 final,因为子类无法重写 final 方法或继承 final 类。

流程图 (CGLIB 代理):

+----------------+
|  Target Object |
| (OrderService) |  <-- 被继承
+----------------+
      ^
      |
+-------------------------------------------------+
|   Proxy Object (由 Spring 在运行时创建)         |
| - 是 Target Object 的一个子类                  |
| - 重写了 Target 中的非 final 方法              |
| - 内部持有一个 MethodInterceptor (包含切面逻辑) |
+-------------------------------------------------+

Spring 如何选择?

  • 传统 Spring/老版本 Spring Boot:如果目标对象实现了接口,默认使用 JDK 动态代理;如果没有,则使用 CGLIB。
  • 现代 Spring Boot (2.x 及以后):为了统一行为和解决一些复杂场景(如类内部调用),默认统一使用 CGLIB 代理,无论目标对象是否实现接口。你也可以通过配置文件 spring.aop.proxy-target-class=false 来改回使用 JDK 代理(如果实现了接口)。

Spring AOP 的完整工作流程

  1. 启动与扫描:Spring 容器启动时,会扫描所有的 Bean 定义和 @Aspect 注解的切面类。

  2. 寻找切点:Spring 解析每个切面中的切点表达式(Pointcut)。

  3. 创建代理:在 Bean 的实例化过程中,Spring 会检查每个 Bean 是否匹配任何切点。

    • 如果一个 Bean 的方法(连接点)匹配了某个切点,Spring 就判定这个 Bean 需要被增强(“advised”)。
    • Spring 不会直接返回这个原始的 Bean 实例,而是会使用 ProxyFactory(代理工厂)为它创建一个代理对象(根据上述规则选择 JDK 或 CGLIB)。
    • 这个代理对象包含了所有匹配的切面逻辑(通知)和一个对原始目标对象的引用。
  4. 注入代理:当其他 Bean 需要依赖这个被增强的 Bean 时(例如通过 @Autowired),Spring 容器注入的是那个代理对象,而不是原始的目标对象

  5. 方法调用与拦截

    • 当外部代码调用代理对象的方法时,调用被代理对象拦截。
    • 代理对象中的拦截器(InvocationHandlerMethodInterceptor)被激活。
    • 拦截器根据配置,按顺序执行切面逻辑(@Before, @Around 等),并在合适的时机通过反射或 super 调用来执行原始目标对象的核心业务方法。
    • 执行完毕后,将结果返回给调用方。

总结

特性JDK 动态代理CGLIB 代理
核心技术Java 反射 (java.lang.reflect.Proxy)字节码增强 (Bytecode Enhancement)
代理方式实现目标对象的所有接口继承目标对象,创建其子类
前提条件目标对象必须实现接口目标对象不能是 final 类,方法不能是 final
Spring Boot 默认是 (推荐)
对代码影响

理解 Spring AOP 是基于运行时动态代理这一核心原理,以及它与目标对象和代理对象的关系,是掌握 AOP 并解决类似“方法内部调用导致 AOP 失效”等问题的关键。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰糖心书房

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值