Spring AOP默认使用 jdk动态代理

在 Spring AOP 中,使用 JDK 动态代理 的性能确实相对 CGLIB 代理稍低。主要原因在于 JDK 动态代理是基于 Java 反射机制实现的,而 CGLIB 是基于字节码操作生成代理类的。不过,Spring 没有选择统一使用 CGLIB,主要有以下几方面原因:

1. JDK 动态代理 vs. CGLIB 代理的性能差异

  • JDK 动态代理 使用 反射机制 来调用目标方法,反射的开销较大,因此性能略逊于直接操作字节码的 CGLIB。
  • CGLIB 通过 **ASM(Java 字节码操作库)**直接操作字节码,生成目标类的子类来实现代理,调用时无需使用反射,性能更高。

在大多数应用中,JDK 动态代理和 CGLIB 代理的性能差异通常是微不足道的,因为代理方法的调用开销相对业务逻辑处理的开销来说是很小的。因此,性能差异不一定会对大多数实际应用造成显著影响。

2. 为什么不统一使用 CGLIB 代理

尽管 CGLIB 在性能上稍优于 JDK 动态代理,但 Spring 并未统一使用 CGLIB 代理,而是优先选择 JDK 动态代理,主要原因有以下几点:

1. JDK 动态代理更轻量

  • JDK 动态代理是 Java 官方提供的,属于 标准库,不需要额外的第三方依赖。
  • 使用 JDK 动态代理时,Spring 可以避免加载和管理 CGLIB 库,从而减小应用的整体依赖,尤其对于不需要大量代理的简单应用,这种方式更轻量化。

2. JDK 动态代理内存开销更小

  • JDK 动态代理基于接口,直接生成一个代理类实例,不涉及生成子类和操作字节码,因此内存开销更小。
  • CGLIB 生成代理类的子类,每个代理类都会占用一定的内存,并且会产生大量的类文件字节码,尤其在高并发和频繁代理生成的情况下,容易导致内存消耗过大。

3. JDK 动态代理更适合接口编程

  • 面向接口编程 是 Java 设计中的最佳实践,优先使用 JDK 动态代理符合 Java 规范,更符合设计规范。
  • Spring 默认优先使用 JDK 动态代理,鼓励开发者使用接口来定义业务逻辑,使得代码解耦性更好,便于测试和扩展。

4. CGLIB 的局限性

  • 不能代理 final 类和 final 方法:CGLIB 是通过生成目标类的子类来实现代理的,而 final 类无法被继承,final 方法也无法被重写,因此 CGLIB 不能代理这些类或方法。
  • 兼容性问题:CGLIB 依赖底层字节码操作库 ASM,不同版本的 JDK 可能会对字节码有不同要求,CGLIB 在某些情况下可能会出现兼容性问题。尽管现代 CGLIB 版本已能兼容大部分 JDK,但 JDK 动态代理在 Java 环境下更加稳定。

5. 性能差距不显著

  • 在大多数情况下,JDK 动态代理的性能已经能够满足需求,性能差异只有在极端高并发的场景下才会被放大。而且 Spring AOP 通常用于较少的、特定的切面操作(如事务、日志等),这些操作本身通常不会频繁发生,也不会对性能造成很大影响。
  • 在需要极致性能的场景下,开发者可以手动选择强制使用 CGLIB(通过 @EnableAspectJAutoProxy(proxyTargetClass = true))。

3. Spring 如何选择代理方式

Spring 中的代理选择策略是优先使用 JDK 动态代理,只有在目标类没有实现接口或者开发者强制指定时,才会使用 CGLIB 代理。这种设计兼顾了 性能兼容性,并且符合 Java 面向接口编程的理念。

4. 什么时候选择 CGLIB 代理

尽管 Spring 默认优先使用 JDK 动态代理,但在以下场景中可以选择使用 CGLIB 代理:

  • 目标类未实现接口:如果目标类没有实现任何接口,Spring 会自动选择 CGLIB 代理。
  • 需要代理具体类的所有方法:如果要代理类的所有方法(包括私有方法和 protected 方法等),可以选择 CGLIB。
  • 强制使用 CGLIB:如果需要 CGLIB 的高性能,或在高并发环境下频繁调用代理方法,可以通过 @EnableAspectJAutoProxy(proxyTargetClass = true) 配置强制使用 CGLIB。

总结

Spring 优先选择 JDK 动态代理,主要是因为它轻量、与 Java 标准库兼容性好、符合面向接口编程的设计理念。尽管 CGLIB 在性能上略优于 JDK 动态代理,但其内存开销更大,并且在代理 final 类和 final 方法时存在局限性。因此,Spring 选择优先使用 JDK 动态代理,只有在特定情况下(如无接口类或性能需求较高)才会使用 CGLIB。

在大多数应用中,JDK 动态代理的性能已经足够满足需求,统一使用 CGLIB 代理带来的性能提升并不足以抵消其带来的内存开销和兼容性问题。

0 0 投票数
Article Rating
订阅评论
提醒
guest
0 评论
最旧
最新 最多投票
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x