通俗易懂!2025 年最新 Spring AOP 切面+通知类型全家福(面试画图+代码,1分钟秒杀版)
一张图记住全部(面试30秒画完满分)
@Aspect ← 切面总开关
┌─────────────────────────────────────────────┐
│ @Pointcut("execution(* com..service.*.*(..))") │ ← 切哪里?(切点)
└─────────────────────────────────────────────┘
↑ ↑ ↑ ↑ ↑
│ │ │ │ │
@Before @After @Around @AfterReturning @AfterThrowing
前戏 后戏 环绕 正常收尾 出事收尾
5种通知类型大白话 + 真实使用频率(2025真实项目)
| 通知类型 | 大白话解释 | 什么时候执行? | 真实项目使用率 | 经典场景 |
|---|---|---|---|---|
| @Before | “先干一件事” | 原方法执行前 | ★★★★ | 参数校验、权限检查、打印请求日志 |
| @After | “最后一定干一件事” | 原方法结束后(不管成功失败) | ★★ | 清理资源(基本被 @AfterReturning 取代) |
| @AfterReturning | “成功了再干一件事” | 原方法正常返回后 | ★★★★ | 打印返回结果、返回数据脱敏 |
| @AfterThrowing | “出错了才干一件事” | 原方法抛异常后 | ★★★★★ | 统一异常日志、报警 |
| @Around | “老子最大!前后都归我管” | 前+后+决定要不要执行原方法 | ★★★★★ | 事务、分布式锁、限流、防重、性能监控 |
2025 年最常用实战写法(直接抄到项目,99%场景够用)
@Aspect
@Component
@Slf4j
public class WebLogAspect {
// 1. 定义切点(可复用)
@Pointcut("execution(* com.example.controller..*.*(..))")
public void controllerPointcut() {}
// 2. @Around 最强王者(事务、锁、限流都用它)
@Around("controllerPointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
String method = pjp.getSignature().toShortString();
Object[] args = pjp.getArgs();
log.info("[请求开始] {} 参数:{}", method, Arrays.toString(args));
try {
Object result = pjp.proceed(); // 真正执行目标方法
log.info("[请求成功] {} 耗时:{}ms 返回:{}", method,
System.currentTimeMillis() - start, result);
return result;
} catch (Exception e) {
log.error("[请求异常] {} 耗时:{}ms", method,
System.currentTimeMillis() - start, e);
throw e;
}
}
// 3. @AfterThrowing 统一异常处理(可替代 @ControllerAdvice)
@AfterThrowing(pointcut = "controllerPointcut()", throwing = "e")
public void afterThrowing(JoinPoint jp, Throwable e) {
log.error("接口出错了!{} {}", jp.getSignature(), e.getMessage());
// 可以发钉钉/企业微信报警
}
}
通知执行顺序(面试必背)
同一个切面内顺序:
@Around(前半部分) → @Before → 目标方法 → @AfterReturning/@AfterThrowing → @After → @Around(后半部分)
多个切面时:看 @Order(n) 数字越小越先执行(默认最低优先级)
@Aspect
@Order(1) // 数字越小越先执行
public class SecurityAspect { ... }
@Aspect
@Order(2)
public class LogAspect { ... }
2025 面试终极6连问 + 标准答案
- 5种通知类型执行顺序是什么?
答:Around前 → Before → 目标方法 → After → (AfterReturning 或 AfterThrowing) → Around后 - @After 和 @AfterReturning 区别?
答:@After 无论成功失败都执行,@AfterReturning 只有成功才执行。 - @Around 一定要调用 proceed() 吗?
答:必须调用!不调用原方法就不执行了(可以用来做权限拦截、灰度发布)。 - 怎么获取方法参数和返回值?
答:ProceedingJoinPoint.getArgs() 获取参数,proceed() 的返回值就是结果。 - 想改返回值怎么办?
答:只能用 @Around,在 proceed() 后包装一层再返回。 - @AfterThrowing 能拿到异常对象吗?
答:能!加 throwing = “e” 参数就能拿到。
终极记忆口诀(10秒背完)
“前 Before,后 Returning,
出事 Throwing,最后 After 定,
Around 最大包前后,
事务锁限流都归它管!”
背完这张图 + 代码 + 口诀 + 6个答案,
面试官让你“手写一个日志切面”,你30秒写完 + 画执行顺序图,
他直接说:“这个小伙子AOP玩得比我还6!”
现在你已经可以轻松手撕所有横切功能了(日志、事务、权限、防重、限流、分布式锁)一把梭!冲!