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
2
3
4
5
6
7
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (shouldProxy(bean)) {
return createProxy(bean);
}
return bean;
}

createProxy() 方法内部:

1
2
3
4
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(bean);
proxyFactory.addAdvice(new TransactionInterceptor());
return proxyFactory.getProxy();

JDK 动态代理实现原理

核心类:java.lang.reflect.Proxy

1
2
3
4
5
6
7
8
9
10
Object proxy = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
System.out.println("Before method: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After method: " + method.getName());
return result;
}
);

执行过程:

  1. 代理类拦截方法调用
  2. 在调用目标方法前后执行增强逻辑
  3. 通过反射调用目标对象

CGLIB 代理实现原理

如果类没有接口,Spring 会用 CGLIB 生成子类:

1
2
3
4
5
6
7
8
9
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TargetClass.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
System.out.println("Before: " + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("After: " + method.getName());
return result;
});
TargetClass proxy = (TargetClass) enhancer.create();

本质:
CGLIB 使用 ASM 生成字节码,创建目标类的子类并重写其方法,在方法前后插入拦截逻辑。

两种代理方式的区别

对比项 JDK 动态代理 CGLIB 动态代理
代理目标 接口
实现方式 反射 + Proxy 字节码生成
性能 反射稍慢 稍快(缓存后)
限制 目标类必须实现接口 目标类不能是 final
包路径 java.lang.reflect.Proxy net.sf.cglib.proxy.Enhancer

AOP 调用链源码精简版

调用代理对象 → 进入 JdkDynamicAopProxy.invoke()

1
2
3
4
5
6
7
8
9
10
11
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 获取拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

// 2. 创建 MethodInvocation 对象
MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, chain);

// 3. 依次执行拦截器
return invocation.proceed();
}

proceed() 会迭代执行所有拦截器,最后再执行目标方法。

常见疑问

疑问 答案
为什么 @Transactional@Async@Cacheable 失效? 因为它们都是 AOP 实现的,需要通过代理调用
为什么同类内部调用事务失效? 自调用没有经过代理,直接调用目标对象方法
怎么强制使用 CGLIB? 在配置类加 @EnableAspectJAutoProxy(proxyTargetClass = true)
代理对象和目标对象的区别? 代理对象包装了目标对象并附加了拦截器链

调试建议

  1. JdkDynamicAopProxy#invoke() 打断点
  2. 观察拦截器链(advised.getInterceptorsAndDynamicInterceptionAdvice()
  3. 查看执行顺序:BeforeAdvice → 目标方法 → AfterReturningAdvice
  4. 如果用 CGLIB,可以调试 CglibAopProxy.DynamicAdvisedInterceptor.intercept()