Skip to content

Spring AOP 原理

Spring AOP 是 Spring 事务、缓存、日志、权限校验等能力的基础。理解它时要抓住代理机制、自调用失效、JDK 动态代理和 CGLIB 区别,以及 AOP 和 Bean 生命周期的关系。


1. AOP 是什么

AOP(Aspect Oriented Programming)是面向切面编程。

它用于把横切逻辑从业务代码中抽离出来,例如:

  • 事务控制。
  • 日志记录。
  • 权限校验。
  • 缓存处理。
  • 接口耗时统计。

核心思想:

text
业务方法只关心业务
公共增强逻辑由代理对象在方法前后织入

2. 核心概念

概念含义
Aspect切面,横切逻辑的集合
JoinPoint连接点,能被增强的位置,Spring AOP 中主要是方法执行
Pointcut切点,匹配哪些方法需要增强
Advice通知,具体增强逻辑
Target目标对象
Proxy代理对象
Weaving织入,把增强逻辑应用到目标对象

3. 通知类型

常见通知:

通知执行时机
@Before方法执行前
@After方法执行后,不管是否异常
@AfterReturning方法正常返回后
@AfterThrowing方法抛异常后
@Around方法执行前后都能控制

@Around 能决定是否调用目标方法,也能修改返回值和处理异常,能力最强。


4. Spring AOP 的实现方式

Spring AOP 基于代理实现,主要有两种代理:

方式适用条件原理
JDK 动态代理目标类实现了接口生成接口代理类
CGLIB目标类没有接口生成目标类子类

默认情况下:

  • 有接口时通常用 JDK 动态代理。
  • 没有接口时使用 CGLIB。
  • 可以通过配置强制使用 CGLIB。

5. JDK 动态代理和 CGLIB 区别

对比点JDK 动态代理CGLIB
基础接口继承
目标类要求必须有接口不能是 final
方法限制代理接口方法不能代理 final 方法
生成对象接口代理对象子类代理对象

如果类或方法是 final,CGLIB 无法通过继承覆盖方法,也就无法增强。


6. 代理对象什么时候创建

AOP 代理通常在 Bean 生命周期后期创建,核心入口和 BeanPostProcessor 有关。

简化流程:

text
实例化 Bean
  -> 属性填充
  -> 初始化前置处理
  -> 初始化方法
  -> 初始化后置处理
  -> 判断是否需要 AOP
  -> 创建代理对象
  -> 容器中暴露代理对象

所以从 Spring 容器中拿到的 Bean,可能不是原始对象,而是代理对象。


7. AOP 调用流程

代理对象调用方法时,流程大致是:

text
调用代理对象方法
  -> 匹配拦截器链
  -> 执行前置通知
  -> 执行目标方法
  -> 执行返回通知或异常通知
  -> 执行后置通知
  -> 返回结果

事务、缓存、日志等增强都是类似的拦截器链模型。


8. 为什么自调用会失效

示例:

java
class UserService {
    public void a() {
        b();
    }

    @Transactional
    public void b() {
        // ...
    }
}

外部调用 a() 时,确实经过代理对象。但 a() 内部调用 b()this.b(),直接调用原始对象方法,没有经过代理对象,因此 b() 上的 AOP 增强不会生效。

解决方式:

  • b() 放到另一个 Bean 中。
  • 从 Spring 容器中获取当前代理对象再调用。
  • 使用 AopContext.currentProxy(),但需要开启暴露代理,不推荐滥用。

9. AOP 和事务的关系

@Transactional 本质上就是基于 AOP 实现的。

简化流程:

text
调用代理方法
  -> 事务拦截器判断事务属性
  -> 开启或加入事务
  -> 执行业务方法
  -> 正常返回:提交事务
  -> 抛出异常:根据规则回滚

所以事务失效场景通常也是 AOP 代理失效场景。


10. 常见失效场景

场景原因
同类内部方法调用没有经过代理对象
方法不是 publicSpring 事务默认主要增强 public 方法
类或方法是 finalCGLIB 无法覆盖
对象不是 Spring Bean容器没有创建代理
异常被吞掉事务拦截器感知不到异常
异常类型不匹配默认只回滚运行时异常和 Error

11. 理解检查

Spring AOP 是编译期织入还是运行期织入?

Spring AOP 主要是运行期基于代理织入。AspectJ 可以支持编译期或类加载期织入。

AOP 能增强 private 方法吗?

Spring AOP 基于代理,通常不能增强 private 方法。因为 private 方法不能被代理对象以覆盖方式拦截。

为什么事务方法内部调用不生效?

因为内部调用走的是 this,没有经过代理对象。


12. 总结

Spring AOP 的核心是代理对象和拦截器链。理解它之后,事务、缓存、自调用失效、JDK 代理和 CGLIB 的区别都会变得清晰。

Released under the MIT License.