AOP的代理模式
AOP的代理模式
1. AOP的实现方式
Spring AOP 主要通过两种动态代理技术实现:
- JDK动态代理:基于接口的代理,要求目标类必须实现至少一个接口。通过反射机制在运行时生成代理类(实现目标接口),并重写接口方法以织入切面逻辑。
- CGLIB动态代理:基于继承的代理,通过字节码增强技术生成目标类的子类,覆盖父类方法并织入切面逻辑。无需目标类实现接口。
2. JDK动态代理与CGLIB动态代理的区别
维度 | JDK动态代理 | CGLIB动态代理 |
实现方式 | 基于接口,通过 | 基于继承,通过 ASM 框架生成目标类的子类 |
限制条件 | 目标类必须实现接口 | 目标类无需实现接口,但不能是 |
性能 | 反射调用方法,初始生成快,但调用略慢 | 直接调用方法,生成代理类慢,但调用更快 |
内存占用 | 代理类轻量,占用较少内存 | 生成的子类较大,可能增加方法区(Metaspace)内存压力 |
3. 性能与扩展性对比
- 性能:
-
- JDK动态代理:在 JDK 8+ 中反射调用经过优化,性能与 CGLIB 差距缩小。适合高频调用的接口方法。
- CGLIB:代理方法调用无需反射,直接通过子类方法覆盖,运行时性能更高(尤其在低版本 JDK 中优势明显)。
- 结论:CGLIB 在方法调用性能上更优,但代理类生成耗时较长。
- 扩展性:
-
- CGLIB 更优:
-
-
- 不依赖接口,可代理任意非
final
类,适用场景更广泛。 - 支持代理类的私有方法(需配置),灵活性更高。
- 不依赖接口,可代理任意非
-
-
- JDK动态代理局限性:仅能代理接口方法,无法覆盖未在接口中声明的方法。
4. 判断依据
- 为什么 CGLIB 扩展性更好?
-
- 无需接口约束:CGLIB 可直接代理无接口的类,适用性覆盖所有非
final
类。 - 方法覆盖全面:能代理目标类的所有非
final
方法(包括非接口方法),而 JDK 代理仅限接口方法。
- 无需接口约束:CGLIB 可直接代理无接口的类,适用性覆盖所有非
- 为什么 CGLIB 性能更优?
-
- 直接方法调用:CGLIB 生成的代理类通过继承覆盖方法,调用时无反射开销。
- JDK 反射瓶颈:JDK 动态代理通过
InvocationHandler.invoke()
反射调用目标方法,存在额外性能损耗(JDK 8+ 已优化,但仍略逊于 CGLIB)。
5. Spring的选择策略
- 默认行为:
-
- 若目标类实现接口,优先使用 JDK动态代理(保持轻量)。
- 若目标类未实现接口,强制使用 CGLIB。
- 强制指定 CGLIB:
可通过@EnableAspectJAutoProxy(proxyTargetClass = true)
强制使用 CGLIB,即使目标类有接口。
AOP的重点就是如何获取到目标类的原始方法,并把它放到特定的上下文环境下执行,对于这一点,jdk代理是采用反射,你看,我都反射了,我还拿不到你的原始方法?直接获取到Method类,再放入特定的上下文环境,也就是AOP通知,调用invoke方法即可。而cglib代理是怎么获得原始方法的呢,它的代理类是继承目标类的,那都直接继承目标类了,那不就可以直接通过super.的形式调用目标类的原始方法