[面试题] Spring AOP 是如何生成代理对象的?JDK 动态代理和 CGLIB 有什么区别?
📖 版块 A:知识讲解版 (温故知新)
-
Spring AOP 的本质:
- Spring AOP 是基于代理模式实现的。
- 它不会直接修改原始类,而是为目标对象生成一个代理对象,再把增强逻辑织入到代理对象的调用链里。
-
生成代理对象的大体流程:
- Spring 在 Bean 初始化阶段,通过自动代理创建器判断当前 Bean 是否需要增强。
- 如果需要,会先收集目标类、Advisor、Interceptor 等信息。
- 再借助
ProxyFactory和AopProxy生成最终代理对象。
-
JDK 动态代理:
- 依赖接口。
- 如果目标类实现了接口,Spring 通常优先使用 JDK 动态代理。
- 代理对象本质上实现了同样的接口。
-
CGLIB:
- 基于继承生成子类代理。
- 目标类没有接口,或者显式要求类代理时,Spring 通常会使用 CGLIB。
final类或final方法无法被 CGLIB 正常增强。
-
一句话区别:
- JDK 动态代理是“基于接口代理”。
- CGLIB 是“基于子类代理”。
🎤 版块 B:面试实战版 (高手逐字稿)
Q:Spring AOP 是如何生成代理对象的?JDK 动态代理和 CGLIB 有什么区别?
💬 高手逐字稿:
Spring AOP 生成代理对象,本质上不是在业务代码执行时临时瞎搞,而是在 Bean 生命周期的初始化阶段,由 Spring 容器统一完成的。
更准确一点说,Spring 的自动代理创建器本身就是一个特殊的 BeanPostProcessor。它会在 postProcessAfterInitialization 这个阶段判断:当前这个 Bean 有没有需要织入的切面。如果没有,就把原始对象原样返回;如果有,就会收集目标类、Advisor、MethodInterceptor 这些信息,然后通过 ProxyFactory 生成一个代理对象,最后把代理对象返回给容器。
也就是说,后续放进容器里、被别的 Bean 注入和调用的,很多时候已经不是原始对象,而是代理对象了。
至于 JDK 动态代理和 CGLIB,本质差异在于生成代理的方式不同。
JDK 动态代理依赖接口,它会生成一个实现了同样接口的代理对象,所以前提是目标类至少有接口可用。 CGLIB 则是通过继承目标类,动态生成一个子类来做代理,所以即使目标类没有接口,也能做增强。
Spring 的默认策略通常是:
- 如果目标类实现了接口,优先走 JDK 动态代理;
- 如果没有接口,或者配置了强制使用类代理,就走 CGLIB。
这里顺手可以补一个容易加分的点:为什么 final 方法经常让 AOP 失效? 因为 CGLIB 是靠子类重写方法织入逻辑的,final 方法没法被重写,自然也就织不进去。
所以这道题最后最好总结成两句:
- Spring AOP 是在 Bean 初始化阶段,通过 BeanPostProcessor 把原始对象替换成代理对象的。
- JDK 动态代理靠接口,CGLIB 靠继承。
最后更新于: