1096 字
约 3 分钟
6
S07-B03 · AOP:切面、代理与失效场景

适用读者:写过 @Aspect 但被"切面不生效"坑过的人。
前置:S07-B01、S07-B02。
实测环境:Spring Boot 3.3.6 / JDK 17(2026-11-05)。
覆盖旧文:968(100) 1737(62) 970(72) 325(54) 474(35) 971(83) = 406 点。
覆盖扩展:E-S07-005(JDK 代理 vs CGLIB)、E-S07-006(自调用失效)、E-S07-020(设计模式)。
上一篇:S07-B02 · Bean 生命周期与作用域
下一篇:S07-B04 · MVC 请求链路。


1. 一句话结论

AOP 的本质是"在 Bean 初始化最后一步(postProcessAfterInitialization)把对象包成代理"。所以:代理之外的调用不走切面——包括"同类内部调用"。

实测(切面日志):

[AOP] 进入 controller 方法
[AOP] controller 返回

(@Before 与 @AfterReturning 都命中,因为请求是从外部进入代理的。)


2. 五种通知与切点

通知 时机
@Before 方法执行前
@AfterReturning 正常返回后
@AfterThrowing 抛异常后
@After 无论如何(finally 语义)
@Around 包住整个调用(可改返回值/异常)

切点常用写法:

@Pointcut("execution(* demo.service..*.*(..))")            // 包及子包
@Pointcut("@annotation(com.demo.Log)")                      // 注解切点
@Pointcut("within(demo.service.*)")                         // 类型内

3. JDK 动态代理 vs CGLIB(E-S07-005)

维度 JDK 动态代理 CGLIB
前提 必须有接口 可代理类(无接口)
实现 InvocationHandler + 生成实现类 生成子类(字节码增强)
限制 只能代理接口方法 不能代理 final 类/方法
性能 调用略快(JDK 8+) 创建略快

Spring Boot 2.x+ 默认 proxyTargetClass=true(CGLIB)——所以"没有接口也能被代理",但 final 方法不会被增强。

判据:"这个方法是不是 final/static/private?" 是 → 切不到。


4. 六种"切面不生效"(A-970)

# 原因 表现
1 同类内部调用(this.method()) 不走代理
2 方法是 private / final / static 无法代理
3 切点表达式写错(包名/类名不匹配) 完全没命中
4 对象不是 Spring 管的(new 出来的) 无代理
5 注解不在代理可见的类上(如注解在实现类而代理接口) 不命中
6 异步/多线程里调用(跨线程丢了代理上下文) 视情况

第 1 条最常踩(E-S07-006):自调用必须走 AopContext.currentProxy() 或拆到另一个 Bean。


5. AOP 的三个真实用途

用途 说明
日志/链路(A-325/A-474) 统一记录入参、耗时、异常
权限校验 注解 + 切面(比拦截器更细)
声明式事务 @Transactional 本身就是 AOP(J01 的失效原因同源)

这解释了一件事:"事务失效"和"AOP 失效"是同一类问题——都是"没走代理"。


6. Spring 里的设计模式(A-971)

模式 在 Spring 里的体现
工厂 BeanFactory
单例 默认作用域(容器内单例)
代理 AOP
模板方法 JdbcTemplate、RestTemplate
观察者 ApplicationEvent(J03)
策略 Resource/HandlerMapping 的多实现
适配器 HandlerAdapter

7. 五个误区

误区 真相
"加了 @Aspect 就一定生效" 切点、代理方式、调用路径都要对
"同类调用也走切面" 不走(self-invocation)
"CGLIB 能代理 final 方法" 不能
"AOP 只有日志用途" 事务、权限、缓存都是它
"AOP 是运行时代码注入" 是代理(对象替换),不是改字节码

8. 自检

  1. AOP 代理在生命周期哪一步生成?
  2. JDK 代理与 CGLIB 的前提差异?
  3. 为什么同类内部调用不走切面?怎么修?
  4. 六种失效原因里,哪三种最常见?
  5. 为什么说"事务失效与 AOP 失效同源"?

9. 下一篇

S07-B04 · Spring MVC:一个请求走过哪些组件

AOP 讲完,下一篇跟一个 HTTP 请求走完全程:DispatcherServlet → 拦截器 → Controller → 返回值处理 → 异常解析。


本篇属于系列《S07 · Spring 框架》。旧文来源:A-968/1737/970/325/474/971;扩展点 E-S07-005/006/020。AOP 日志为 Spring Boot 3.3.6 实测。

S07-B03 · AOP:切面、代理与失效场景
https://www.clxhxhhr.top/posts/5270/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。