什么是动态代理?
动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。
在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。
spring aop 支持以下两种动态代理方式:
- jdk 动态代理 (jdk dynamic proxy)
- cglib 代理 (code generation library)
spring 框架会根据你的业务对象(目标对象)的情况,智能地选择其中一种来创建代理。
1. jdk 动态代理
- 技术基础:java 官方提供的
java.lang.reflect.proxy
类和java.lang.reflect.invocationhandler
接口,是 java 原生支持的。 - 实现方式:在运行时,创建一个新的代理类,这个代理类会实现目标对象所实现的所有接口。
- 硬性要求:被代理的目标对象必须实现至少一个接口。 如果一个类没有实现任何接口,jdk 动态代理就无法为其创建代理。
- 工作流程:当你调用代理对象的方法时,这个调用会被转发到
invocationhandler
的invoke()
方法中。spring aop 在invoke()
方法内部织入了你的切面逻辑(如@before
、@after
等),然后再通过 java 的反射机制调用原始目标对象的方法。
2. cglib 代理
- 技术基础:一个强大的第三方代码生成库(spring 内部集成了它),它通过字节码增强 (bytecode enhancement) 技术来工作。
- 实现方式:在运行时,动态地创建一个被代理对象的子类作为代理对象。
- 硬性要求:被代理的目标类不能是
final
类,需要被代理的方法也不能是final
或private
,因为子类无法继承final
类或重写final
/private
方法。 - 工作流程:代理类会重写(override)父类(即你的目标对象)中所有非
final
的方法。在这些重写的方法里,spring aop 织入了切面逻辑,然后再通过调用super.method()
来执行原始目标对象的业务逻辑。
spring 如何选择?
这是面试中的高频问题,因为这个默认行为在不同版本中有所变化。
场景 | spring 的选择 |
---|---|
目标对象实现了接口 | 在老的 spring 版本或传统 spring xml 配置中,默认使用 jdk 动态代理。 在spring boot (2.x 及以后) 中,为了统一行为和解决一些代理问题,默认依然使用 cglib。 |
目标对象没有实现接口 | 无论在哪个版本,都只能使用 cglib。 |
为什么 spring boot 默认使用 cglib?
主要原因是为了解决“方法自调用时 aop 失效”的问题,并提供更一致的行为。使用 cglib 可以确保即使目标对象实现了接口,代理的也是类本身,这在处理一些复杂的依赖注入和内部调用场景时更加可靠。
当然,你也可以通过在 application.properties
中进行配置来改变这个默认行为:
# 如果设置为 true (默认值),则统一使用 cglib # 如果设置为 false,则在目标对象实现接口时,会优先使用 jdk 动态代理 spring.aop.proxy-target-class=true
总结对比
特性 | jdk 动态代理 | cglib 代理 |
---|---|---|
代理方式 | 基于接口 (实现共同接口) | 基于继承 (创建子类) |
前提条件 | 目标对象必须实现接口 | 目标对象不能是 final 类 |
性能 | 在早期版本中,通过反射调用性能略低于 cglib。但在目前jdk 版本中,两者性能差距已经非常小。 | 性能通常被认为略高,因为它直接操作字节码并调用 super 。 |
spring boot 默认 | 否 | 是 |
到此这篇关于spring aop 支持哪两种动态代理方式?的文章就介绍到这了,更多相关spring aop动态代理内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论