Spring 切面定义与通知类型(Before、After、Around等)

通俗易懂!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连问 + 标准答案

  1. 5种通知类型执行顺序是什么?
    答:Around前 → Before → 目标方法 → After → (AfterReturning 或 AfterThrowing) → Around后
  2. @After 和 @AfterReturning 区别?
    答:@After 无论成功失败都执行,@AfterReturning 只有成功才执行。
  3. @Around 一定要调用 proceed() 吗?
    答:必须调用!不调用原方法就不执行了(可以用来做权限拦截、灰度发布)。
  4. 怎么获取方法参数和返回值?
    答:ProceedingJoinPoint.getArgs() 获取参数,proceed() 的返回值就是结果。
  5. 想改返回值怎么办?
    答:只能用 @Around,在 proceed() 后包装一层再返回。
  6. @AfterThrowing 能拿到异常对象吗?
    答:能!加 throwing = “e” 参数就能拿到。

终极记忆口诀(10秒背完)

“前 Before,后 Returning,
出事 Throwing,最后 After 定,
Around 最大包前后,
事务锁限流都归它管!”

背完这张图 + 代码 + 口诀 + 6个答案,
面试官让你“手写一个日志切面”,你30秒写完 + 画执行顺序图,
他直接说:“这个小伙子AOP玩得比我还6!”
现在你已经可以轻松手撕所有横切功能了(日志、事务、权限、防重、限流、分布式锁)一把梭!冲!

文章已创建 3958

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部