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.