---
title: Spring AOP 核心：七大概念、代理选型与失效场景
type: concept
domain: spring
tags: [spring, aop, proxy, cglib, jdk-dynamic-proxy, pointcut]
status: learning
created: 2026-09-28
last_reviewed: 2026-09-28
method: feynman
related_questions:
  - "介绍一下 Spring 的 AOP"
related_knowledge:
  - ./spring-ioc-core.md
  - ./spring-transaction-core.md
visualizations: []
anki_cards: 7
interview_rounds:
  - "2026-09-28-round-1"
---

# Spring AOP 核心：七大概念、代理选型与失效场景

> Spring AOP 的七大核心概念、JDK/CGLIB 代理选型、五大失效场景与规避方案

## TL;DR（3-5 句话，面试前 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 强制 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 种规避方案

1. **注入自身代理**：
   ```java
   @Service
   public class ServiceA {
       @Autowired
       @Lazy
       private ServiceA self;
       
       public void methodA() {
           self.methodB(); // 经过代理
       }
       
       @Transactional
       public void methodB() { ... }
   }
   ```

2. **AopContext.currentProxy()**：
   ```java
   ((ServiceA) AopContext.currentProxy()).methodB();
   ```
   （需要 `@EnableAspectJAutoProxy(exposeProxy = true)`）

3. **反射调用**：
   ```java
   Method method = getClass().getMethod("methodB");
   method.invoke(getAopProxy(), args);
   ```

4. **拆分成两个类**：
   ```java
   @Service
   public class ServiceA {
       @Autowired
       private ServiceB serviceB;
       
       public void methodA() {
           serviceB.methodB(); // 经过代理
       }
   }
   
   @Service
   public class ServiceB {
       @Transactional
       public void methodB() { ... }
   }
   ```

### STEP 4 · 简化（比喻/口诀）

> **AOP 七大概念口诀**：连接点、切点、通知、切面、目标、引入、织入（7 个概念记 7 个字）。

> **代理选型口诀**：有接口用 JDK，无接口用 CGLIB；Spring Boot 2.x 强制 CGLIB。

> **五大失效口诀**：自调用、非 public、final/static、未注册、未扫描（5 个场景记 5 个字）。

## 常见误区（从面试记录提炼）

- **误区 1**："Spring AOP 是编译期织入" → 正确说法：Spring AOP 是**运行时织入**，AspectJ 才是编译期/加载期织入
- **误区 2**："JDK 代理比 CGLIB 性能好" → 正确说法：JDK 代理略快，但 CGLIB 更灵活（无需接口）
- **误区 3**："自调用会导致 AOP 失效" → 正确说法：**确实会失效**（不经过代理），需要 4 种规避方案
- **误区 4**："Spring Boot 2.x 默认用 JDK 代理" → 正确说法：Spring Boot 2.x 默认 `spring.aop.proxy-target-class=true` 强制 CGLIB

## 延伸追问（面试追问预演）

1. **追问 A**：Spring AOP 和 AspectJ 有什么区别？
   - 要点：Spring AOP 是运行时织入，AspectJ 是编译期/加载期织入；AspectJ 支持更多织入点（字段、构造器、方法调用等）

2. **追问 B**：为什么 Spring Boot 2.x 默认强制 CGLIB？
   - 要点：避免类型转换问题（JDK 代理返回接口类型，CGLIB 返回目标类类型）

3. **追问 C**：自调用导致 AOP 失效的 4 种规避方案是什么？
   - 要点：注入自身代理（@Lazy / @Resource）、AopContext.currentProxy()、反射调用、拆分成两个类

4. **追问 D**：切点表达式的 execution 语法是什么？
   - 要点：`execution(modifiers? ret-type? declaring-type? name(param-types) throws?)`

## 速查表（面试前 30 秒扫完）

```
AOP 七大概念 · 连接点/切点/通知/切面/目标/引入/织入
代理选型 · 有接口用 JDK 无接口用 CGLIB · Spring Boot 2.x 强制 CGLIB
切点表达式 · execution/@within/@annotation/@args
五大失效 · 自调用/非public/final-static/未注册/未扫描
自调用规避 · 注入自身代理/AopContext/反射/拆分两类
常见错点：Spring AOP 编译期织入 ❌ / 自调用不影响 AOP ❌ / Spring Boot 2.x 用 JDK 代理 ❌
```

## 关联题目（题库）

- [ ] 《介绍一下 Spring 的 AOP》—— 2026-09-28 Round 1, 评分 ⭐⭐⭐

## 关联知识

- [Spring IoC 核心](./spring-ioc-core.md)
- [Spring 事务核心](./spring-transaction-core.md)

## Anki 候选卡片

以下知识点适合做 Anki 卡片：

1. **正**：Spring AOP 的七大核心概念是什么？**反**：JoinPoint / Pointcut / Advice / Aspect / Target / Introduction / Weaving
2. **正**：JDK 代理和 CGLIB 的区别是什么？**反**：JDK 基于接口，CGLIB 基于继承；JDK 要求目标类实现接口，CGLIB 要求目标类不能是 final
3. **正**：Spring Boot 2.x 默认使用哪种代理？**反**：默认 `spring.aop.proxy-target-class=true` 强制 CGLIB
4. **正**：Spring AOP 的五大失效场景是什么？**反**：自调用、private/final/static 方法、非 public、未注册 Bean、切面未扫描
5. **正**：自调用导致 AOP 失效的 4 种规避方案是什么？**反**：注入自身代理（@Lazy / @Resource）、AopContext.currentProxy()、反射调用、拆分成两个类
6. **正**：切点表达式的 4 种类型是什么？**反**：execution / @within / @annotation / @args
7. **正**：Spring AOP 和 AspectJ 的区别是什么？**反**：Spring AOP 是运行时织入，AspectJ 是编译期/加载期织入；AspectJ 支持更多织入点

---

*最后更新：2026-09-28*
