
Spring AOP 主要基于 动态代理 (Dynamic Proxy) 技术实现。
它不会修改你的源代码(.java 文件),也不会在编译时修改字节码(.class 文件),而是在应用程序运行时,动态地为你需要增强的对象创建一个“代理”对象。我们最终调用和交互的,就是这个代理对象。
具体来说,Spring AOP 会根据情况自动选择以下两种动态代理技术中的一种:
1. JDK 动态代理 (JDK Dynamic Proxy)
这是 Java 官方提供的代理技术。
- 实现方式:利用
java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。 - 工作前提:目标对象必须实现至少一个接口。
- 工作原理:Spring AOP 会在运行时创建一个新的代理类,这个代理类实现了目标对象所实现的所有接口。当你调用代理对象的任何方法时,这个调用会被转发到
InvocationHandler的invoke方法中。在这个invoke方法里,Spring AOP 框架就有机会织入你的切面逻辑(比如执行@Before通知),然后再通过反射调用你原始目标对象的真正方法。
简单来说: 如果你的 UserServiceImpl 实现了 UserService 接口,Spring 就会创建一个代理对象,这个代理对象也实现了 UserService 接口,它内部“包裹”着你真正的 UserServiceImpl 实例。
2. CGLIB (Code Generation Library)
这是一个强大的、高性能的第三方代码生成库。
- 实现方式:通过字节码增强 (Bytecode Enhancement) 技术。
- 工作前提:目标对象不需要实现接口。
- 工作原理:CGLIB 会在运行时动态地创建一个目标对象的子类作为代理对象。然后,它会重写 (Override) 目标类中所有可以被重写的方法(即非
final的方法)。在这些重写的代理方法中,它会织入你的切面逻辑,然后再通过调用super.method()去执行原始父类(也就是你的目标对象)的业务逻辑。
简单来说: 如果你的 OrderService 类没有实现任何接口,Spring 就会利用 CGLIB 创建一个 OrderService 的子类(例如 OrderService$$EnhancerBySpringCGLIB)作为代理。
Spring 如何选择?
这是一个常见的面试题:
- 在老的 Spring 版本或传统配置中:如果目标对象实现了接口,默认使用 JDK 动态代理;如果目标对象没有实现接口,则使用 CGLIB。
- 在现代的 Spring Boot (2.x 及以后) 中:为了解决类内部方法调用AOP失效等问题并统一行为,默认统一使用 CGLIB,无论你的类是否实现了接口。当然,你也可以通过配置
spring.aop.proxy-target-class=false来强制在有接口的情况下使用 JDK 动态代理。
总结表格
| 特性 | JDK 动态代理 | CGLIB |
|---|---|---|
| 核心技术 | Java 反射 | 字节码增强 |
| 代理方式 | 实现目标对象的接口 | 继承目标对象,创建其子类 |
| 前提条件 | 目标对象必须实现接口 | 目标对象不能是 final 类,方法不能是 final |
| Spring Boot 默认 | 否 | 是 (推荐) |
总结:
Spring AOP 的魔法在于运行时动态代理。它通过 JDK 动态代理(基于接口)或 CGLIB(基于继承)来创建一个“包装”你原始对象的代理,并在这个代理中神不知鬼不觉地织入了你的横切逻辑,而这一切都无需你修改任何一行业务代码。

278

被折叠的 条评论
为什么被折叠?



