Spring AOP 原理详解(JDK 动态代理 & CGLIB)
Spring AOP 原理详解(JDK 动态代理 & CGLIB)
什么是 AOP?
AOP(Aspect-Oriented Programming)
➡️中文:面向切面编程
它让我们可以在不修改业务代码的前提下,统一地为方法添加“横切逻辑”,比如:
- 日志记录
- 权限控制
- 性能监控
- 事务管理
换句话说,AOP = 在方法执行的前后织入增强逻辑
Spring AOP 的本质
Spring AOP 本质上是通过 动态代理(Dynamic Proxy) 实现的。
代理方式 | 触发条件 | 底层实现 | 特点 |
---|---|---|---|
JDK 动态代理 | 目标类实现了接口 | java.lang.reflect.Proxy |
代理类只实现接口方法 |
CGLIB 动态代理 | 目标类没有实现接口 | 通过字节码生成子类 | 可代理所有非 final 方法 |
AOP 调用链示意图
graph TD
A["Client 调用代理对象方法"] --> B["AOP Proxy 拦截"]
B --> C["AdvisedSupport (通知配置)"]
C --> D["MethodInterceptor 链"]
D --> E["执行目标方法"]
D --> F["Before Advice"]
D --> G["After Advice"]
D --> H["Around Advice"]
核心接口结构
Spring 的 AOP 由以下几个核心组件组成:
接口/类 | 作用 |
---|---|
ProxyFactory | 用来创建代理对象 |
AdvisedSupport | 保存目标对象与拦截器链 |
MethodInterceptor | 方法拦截器,定义增强逻辑 |
AopProxy | 真正执行代理逻辑的接口(JDK/CGLIB) |
Advisor | 组合切点与通知的容器 |
创建代理的源码路径
当 Spring 检测到一个 Bean 上有切面(如 @Transactional
、@Aspect
),
它会在 Bean 初始化时进入:
1 | AnnotationAwareAspectJAutoProxyCreator.postProcessAfterInitialization() |
关键方法:
1 |
|
createProxy() 方法内部:
1 | ProxyFactory proxyFactory = new ProxyFactory(); |
JDK 动态代理实现原理
核心类:java.lang.reflect.Proxy
1 | Object proxy = Proxy.newProxyInstance( |
执行过程:
- 代理类拦截方法调用
- 在调用目标方法前后执行增强逻辑
- 通过反射调用目标对象
CGLIB 代理实现原理
如果类没有接口,Spring 会用 CGLIB 生成子类:
1 | Enhancer enhancer = new Enhancer(); |
本质:
CGLIB 使用 ASM 生成字节码,创建目标类的子类并重写其方法,在方法前后插入拦截逻辑。
两种代理方式的区别
对比项 | JDK 动态代理 | CGLIB 动态代理 |
---|---|---|
代理目标 | 接口 | 类 |
实现方式 | 反射 + Proxy | 字节码生成 |
性能 | 反射稍慢 | 稍快(缓存后) |
限制 | 目标类必须实现接口 | 目标类不能是 final |
包路径 | java.lang.reflect.Proxy |
net.sf.cglib.proxy.Enhancer |
AOP 调用链源码精简版
调用代理对象 → 进入 JdkDynamicAopProxy.invoke()
:
1 |
|
proceed()
会迭代执行所有拦截器,最后再执行目标方法。
常见疑问
疑问 | 答案 |
---|---|
为什么 @Transactional 、@Async 、@Cacheable 失效? |
因为它们都是 AOP 实现的,需要通过代理调用 |
为什么同类内部调用事务失效? | 自调用没有经过代理,直接调用目标对象方法 |
怎么强制使用 CGLIB? | 在配置类加 @EnableAspectJAutoProxy(proxyTargetClass = true) |
代理对象和目标对象的区别? | 代理对象包装了目标对象并附加了拦截器链 |
调试建议
- 在
JdkDynamicAopProxy#invoke()
打断点 - 观察拦截器链(
advised.getInterceptorsAndDynamicInterceptionAdvice()
) - 查看执行顺序:
BeforeAdvice
→ 目标方法 →AfterReturningAdvice
- 如果用 CGLIB,可以调试
CglibAopProxy.DynamicAdvisedInterceptor.intercept()
All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.