TL;DR(30 秒扫完)
- AOP 本质:基于代理的切面编程增强,在方法前后织入横切逻辑
- 七大核心概念:JoinPoint / Pointcut / Advice / Aspect / Target / Introduction / Weaving
- 代理选型:Spring 5+ 默认有接口用 JDK、无接口用 CGLIB;Spring Boot 2.x 默认
spring.aop.proxy-target-class=true强制 CGLIB - 五大失效场景:自调用、private/final/static 方法、非 public、未注册 Bean、切面未扫描
关键结论
结论 A(七大概念)JoinPoint(连接点)/ Pointcut(切点)/ Advice(通知)/ Aspect(切面)/ Target(目标)/ Introduction(引入)/ Weaving(织入)
结论 B(代理选型)JDK 代理基于接口,CGLIB 基于继承;Spring 5+ 默认有接口用 JDK 无接口用 CGLIB
结论 C(Spring Boot 2.x)默认
spring.aop.proxy-target-class=true 强制 CGLIB结论 D(五大失效场景)自调用、private/final/static 方法、非 public、未注册 Bean、切面未扫描
完整讲解(费曼四步)
STEP 1 · 概念(一句话定义)
- AOP(面向切面编程):基于代理的切面编程增强,在方法前后织入横切逻辑(如日志、事务、权限)
- 切面(Aspect):横切关注点的模块化(如日志切面、事务切面)
- 通知(Advice):在连接点执行的动作(前置、后置、环绕、异常)
STEP 2 · 大白话(生活类比)
类比(AOP):就像快递公司的"签收流程"——你在收快递时(方法执行),快递员会自动记录签收时间(前置通知)、拍照留证(后置通知)、异常时上报(异常通知),整个过程你不需要参与(切面逻辑与业务逻辑解耦)。
STEP 3 · 底层(细节 + 代码/表格)
3.1 七大核心概念
| 概念 | 定义 | 示例 |
|---|---|---|
| JoinPoint | 程序执行的某个点(方法执行) | 一个方法执行就是一个 JoinPoint |
| Pointcut | 定义哪些 JoinPoint 需要织入 | execution( com.example.service..*(..)) |
| Advice | 在 JoinPoint 执行的动作 | @Before / @After / @Around / @AfterThrowing |
| Aspect | 切面 = 切点 + 通知 | 一个类标注 @Aspect |
| Target | 被代理的目标对象 | 被 @Aspect 增强的类 |
| Introduction | 给目标类添加新的接口/属性 | Spring AOP 不支持 |
| Weaving | 将切面织入到目标对象的过程 | 运行时织入(Spring AOP)vs 编译期织入(AspectJ) |
3.2 代理选型
| 特性 | JDK 动态代理 | CGLIB |
|---|---|---|
| 原理 | 基于接口生成代理类 | 基于继承生成子类 |
| 要求 | 目标类必须实现接口 | 目标类不能是 final |
| 性能 | 略快 | 略慢(生成子类有开销) |
| Spring 5+ 默认 | 有接口用 JDK | 无接口用 CGLIB |
| Spring Boot 2.x | 默认关闭 | 默认 spring.aop.proxy-target-class=true 强制 CGLIB |
- 因为 Spring Boot 2.x 默认使用
@EnableAspectJAutoProxy(proxyTargetClass = true) - 这样即使目标类有接口,也使用 CGLIB 代理,避免类型转换问题
3.3 切点表达式
| 表达式 | 含义 | 示例 |
|---|---|---|
| execution | 匹配方法执行 | execution( com.example.service..*(..)) |
| @within | 匹配标注了某注解的类 | @within(com.example.annotation.Log) |
| @annotation | 匹配标注了某注解的方法 | @annotation(org.springframework.web.bind.annotation.GetMapping) |
| @args | 匹配参数类型 | @args(java.lang.String) |
3.4 五大失效场景
| 失效场景 | 原因 | 规避方案 |
|---|---|---|
| 自调用 | 类内部方法调用不经过代理 | 注入自身代理(@Lazy / @Resource 注入自己) |
| private/final/static 方法 | CGLIB 无法继承 final 类,JDK 代理不处理非 public 方法 | 方法改为 public、非 final |
| 非 public 方法 | JDK 代理只处理 public 方法 | 方法改为 public |
| 未注册 Bean | AOP 代理只在 Spring 容器中生效 | 确保目标类被 @Component 标注 |
| 切面未扫描 | @Aspect 类未被 Spring 扫描 | 确保 @Aspect 类在 @ComponentScan 范围内 |
3.5 自调用的 4 种规避方案
@EnableAspectJAutoProxy(exposeProxy = true))
STEP 4 · 简化(比喻/口诀)
AOP 七大概念口诀:连接点、切点、通知、切面、目标、引入、织入(7 个概念记 7 个字)。
代理选型口诀:有接口用 JDK,无接口用 CGLIB;Spring Boot 2.x 强制 CGLIB。
五大失效口诀:自调用、非 public、final/static、未注册、未扫描(5 个场景记 5 个字)。
速查表
AOP 七大概念 · 连接点/切点/通知/切面/目标/引入/织入
代理选型 · 有接口用 JDK 无接口用 CGLIB · Spring Boot 2.x 强制 CGLIB
切点表达式 · execution/@within/@annotation/@args
五大失效 · 自调用/非public/final-static/未注册/未扫描
自调用规避 · 注入自身代理/AopContext/反射/拆分两类
常见错点:Spring AOP 编译期织入 ❌ / 自调用不影响 AOP ❌ / Spring Boot 2.x 用 JDK 代理 ❌
Anki 候选卡片
Q: Spring AOP 的七大核心概念是什么?
A: JoinPoint / Pointcut / Advice / Aspect / Target / Introduction / Weaving
Q: JDK 代理和 CGLIB 的区别是什么?
A: JDK 基于接口,CGLIB 基于继承;JDK 要求目标类实现接口,CGLIB 要求目标类不能是 final
Q: Spring Boot 2.x 默认使用哪种代理?
A: 默认
spring.aop.proxy-target-class=true 强制 CGLIBQ: Spring AOP 的五大失效场景是什么?
A: 自调用、private/final/static 方法、非 public、未注册 Bean、切面未扫描
Q: 自调用导致 AOP 失效的 4 种规避方案是什么?
A: 注入自身代理(@Lazy / @Resource)、AopContext.currentProxy()、反射调用、拆分成两个类
Q: 切点表达式的 4 种类型是什么?
A: execution / @within / @annotation / @args
Q: Spring AOP 和 AspectJ 的区别是什么?
A: Spring AOP 是运行时织入,AspectJ 是编译期/加载期织入;AspectJ 支持更多织入点
关联题目
关联知识
AOP 七大核心概念、JDK/CGLIB 代理选型、五大失效场景与规避方案
✦ 记 忆 口 诀 ✦
七大概念七字记 / 有接口用 JDK 无接口用 CGLIB / 五大失效场景
关键可视化
暂无可视化
知识关系
⬆️ 前置(Prerequisite)
spring-ioc-core🔄 延伸(Extends)
暂无⚡ 对比(Contrast)
暂无
🎯 概念
📏 规则
⚠️ 误区
🔍 追问
✨ 口诀
共 0 张卡,点击翻面