通俗易懂!Spring AOP 一次讲透(2025 面试+实战版,背完直接画图秒杀)
一句话记住 AOP 是干嘛的:
AOP 就是让你在不改原代码的情况下,横着切一刀,统一给几百个方法加“前奏”和“尾声”。
典型例子:所有接口都要打印日志、校验权限、开事务 → 用 AOP 一行代码搞定!
现实中的经典比喻(记住这个就忘不了)
你开演唱会:
- 原代码 = 歌手唱歌(核心业务)
- AOP = 舞台灯光、音响、安保、售票、录像(横切关注点)
你不需要让每个歌手自己去管灯光、安保,交给专业团队(AOP)统一搞定就行!
Spring AOP 能干的 7 大常用场景(2025 年真实项目 100% 在用)
| 排名 | 场景 | 具体干啥 | 真实代码示例(直接抄) |
|---|---|---|---|
| 1 | 统一日志打印 | 入参、出参、耗时、异常 | @Around + 打印 proceed 前后 |
| 2 | 事务管理 @Transactional | 自动开启/提交/回滚事务 | Spring 自己就是用 AOP 实现的! |
| 3 | 权限校验 | 只有管理员能调某个接口 | @PreAuthorize(“hasRole(‘ADMIN’)”) |
| 4 | 防止重复提交(防重) | 同一个用户 3 秒内点两次提交只生效一次 | 自定义 @NoRepeatSubmit + Redis 锁 |
| 5 | 分布式锁 | 高并发下单用 Redisson 锁 | @Around + RLock.lock() |
| 6 | 限流 | 接口每秒只能访问 100 次 | 自定义 @RateLimit + Sentinel/Redis |
| 7 | 全局异常处理 | 所有 Controller 抛异常统一转成 {code,msg} 格式 | @ControllerAdvice(底层也是 AOP) |
Spring AOP 核心概念口诀(5 个名词,10 秒背会)
“切面 Aspect,切点 Pointcut,
通知 Advice(前中后环异常),
连接点 JoinPoint,织入 Weaving”
| 名词 | 大白话解释 | 代码里怎么写 |
|---|---|---|
| Aspect(切面) | “我要干的横切逻辑”总开关 | @Aspect + @Component |
| Pointcut(切点) | “到底切哪里”?要切哪些方法 | @Pointcut(“execution(* com..service..(..))”) |
| Advice(通知) | “到底什么时候干”?前/后/环绕/异常后 | @Before、@After、@Around、@AfterThrowing |
| JoinPoint | 被切到的那个方法本身 | ProceedingJoinPoint pjp = 环绕时参数 |
| Weaving(织入) | 把切面插进原代码的过程(Spring 用代理) | 运行时生成代理对象(CGLIB/JDK 动态代理) |
2025 年最常用 3 种写法(直接抄到项目)
@Aspect
@Component
public class WebLogAspect {
// 1. 切所有 Controller 包下的方法(最常用)
@Pointcut("execution(* com.example.controller..*.*(..))")
public void webLog() {}
// 2. 环绕通知(最强大,能控制是否执行原方法)
@Around("webLog()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object result = pjp.proceed(); // 真正执行目标方法
long time = System.currentTimeMillis() - start;
// 打印请求日志(入参、出参、耗时)
log.info("请求:{},耗时:{}ms", pjp.getSignature(), time);
return result;
}
// 3. 异常通知(统一捕获异常)
@AfterThrowing(pointcut = "webLog()", throwing = "e")
public void doAfterThrowing(JoinPoint jp, Throwable e) {
log.error("接口出错了:{}", jp.getSignature(), e);
}
}
Spring AOP vs AspectJ(面试必问)
| 项目 | Spring AOP | AspectJ(核弹) |
|---|---|---|
| 织入方式 | 运行时代理(只能切 public 方法) | 编译期/加载期织入(能切 private、字段) |
| 性能 | 稍慢(多一层代理) | 最快 |
| 学习成本 | 简单,5 分钟上手 | 复杂,学点表达式很难 |
| 2025 项目使用率 | 99.99% | 0.01%(基本没人用) |
终极面试 6 连问 + 标准答案
- AOP 核心思想是什么?
→ 把重复代码抽出来横向切入,保持业务代码干净。 - @Transactional 底层怎么实现的?
→ Spring AOP + Proxy + TransactionInterceptor。 - 为什么只能切 public 方法?
→ Spring AOP 用动态代理,只能代理接口或类的 public 方法。 - 同一个类内部方法调用,AOP 会生效吗?
→ 不会!因为没走代理,直接 this.method()。解决:@Autowired 自己注入自己 或 用 AopContext.currentProxy()。 - 环绕通知 @Around 一定要写 proceed() 吗?
→ 必须!不写就不执行原方法了(可以用来做权限拦截)。 - Spring Boot 项目怎么开启 AOP?
→ 加个 @EnableAspectJAutoProxy 就行,但 Boot 项目默认就开了!直接写 @Aspect 就行。
背完这张表 + 3 个代码示例 + 口诀,
面试官问“讲讲 Spring AOP”,你 1 分钟画图 + 甩代码 + 背场景,直接满分!
现在你已经可以轻松手撕日志、事务、权限、分布式锁等所有横切功能了!冲!