ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AOP 通知的类型详解

AOP 通知的类型详解 AOP 通知的类型详解通知Advice是切面在切点上执行的具体动作它定义了“在什么时机做什么”。Spring AOP 提供了五种通知类型每种对应不同的执行时机和适用场景。一、五种通知总览通知类型注解执行时机能否获取返回值能否获取异常能否阻止目标方法前置通知Before目标方法执行前❌❌❌后置通知After目标方法执行后无论异常❌❌❌返回通知AfterReturning目标方法正常返回后✅❌❌异常通知AfterThrowing目标方法抛出异常后❌✅❌环绕通知Around目标方法执行前后✅✅✅二、前置通知 Before在目标方法执行之前执行。适合做参数校验、日志记录、权限检查等准备工作。Before(execution(* com.example.service.*.*(..)))publicvoidbefore(JoinPointjoinPoint){StringmethodNamejoinPoint.getSignature().getName();Object[]argsjoinPoint.getArgs();System.out.println(开始执行methodName参数Arrays.toString(args));}特点无法阻止目标方法执行除非抛出异常。无法获取返回值。无法获取异常。如果需要修改参数只能用Around。适用场景记录方法调用日志参数合法性校验权限检查不通过则抛异常设置 MDC 上下文注意Before中抛出异常会阻止目标方法执行但不会触发AfterThrowing因为AfterThrowing只捕获目标方法本身的异常。三、后置通知 After在目标方法执行之后执行无论目标方法正常返回还是抛出异常。After(execution(* com.example.service.*.*(..)))publicvoidafter(JoinPointjoinPoint){System.out.println(执行结束joinPoint.getSignature().getName());}特点类似finally块一定会执行。无法获取返回值。无法获取异常。无法阻止目标方法执行。适用场景清理资源如关闭连接、释放锁记录方法执行结束日志统计方法调用次数注意After和AfterReturning的区别在于After无论是否异常都会执行AfterReturning只在正常返回时执行。如果同时使用AfterReturning先执行After后执行。四、返回通知 AfterReturning在目标方法正常返回后执行可以获取返回值。AfterReturning(pointcutexecution(* com.example.service.*.*(..)),returningresult)publicvoidafterReturning(JoinPointjoinPoint,Objectresult){System.out.println(返回结果result);}特点只在目标方法正常返回时执行。可以获取返回值通过returning属性指定参数名。可以修改返回值不能直接修改但可以替换返回对象的内容。如果目标方法抛出异常不会执行。returning 属性的类型匹配// 只匹配返回 String 的方法AfterReturning(pointcutserviceLayer(),returningresult)publicvoidafterReturning(JoinPointjp,Stringresult){// 如果目标方法返回的不是 String此通知不执行}// 匹配任意返回值AfterReturning(pointcutserviceLayer(),returningresult)publicvoidafterReturning(JoinPointjp,Objectresult){// 所有正常返回都会执行}适用场景记录方法返回值日志对返回值做后处理如脱敏、格式化缓存结果注意returning指定的参数名必须与切点表达式中的名称一致且类型要匹配。如果类型不匹配通知不会执行。五、异常通知 AfterThrowing在目标方法抛出异常后执行可以获取异常对象。AfterThrowing(pointcutexecution(* com.example.service.*.*(..)),throwingex)publicvoidafterThrowing(JoinPointjoinPoint,Exceptionex){System.out.println(执行异常joinPoint.getSignature().getName()异常信息ex.getMessage());}特点只在目标方法抛出异常时执行。可以获取异常对象通过throwing属性指定参数名。无法处理异常异常会继续向上抛出只能记录。无法获取返回值。throwing 属性的类型过滤// 只捕获 IllegalArgumentExceptionAfterThrowing(pointcutserviceLayer(),throwingex)publicvoidafterThrowing(JoinPointjp,IllegalArgumentExceptionex){// 只有 IllegalArgumentException 才会触发}// 捕获所有异常AfterThrowing(pointcutserviceLayer(),throwingex)publicvoidafterThrowing(JoinPointjp,Exceptionex){// 所有 Exception 子类都会触发}// 捕获 ThrowableAfterThrowing(pointcutserviceLayer(),throwingex)publicvoidafterThrowing(JoinPointjp,Throwableex){// 所有异常和错误都会触发}适用场景统一记录异常日志异常监控和告警异常信息收集注意AfterThrowing不能吞掉异常异常会继续向上抛出。如果需要处理异常吞掉或转换必须用Around。六、环绕通知 Around功能最全的通知可以在目标方法执行前后都插入逻辑控制目标方法是否执行、修改参数、修改返回值、处理异常。Around(execution(* com.example.service.*.*(..)))publicObjectaround(ProceedingJoinPointpjp)throwsThrowable{StringmethodNamepjp.getSignature().getName();longstartSystem.currentTimeMillis();Objectresult;try{// 前置逻辑System.out.println(开始执行methodName);// 执行目标方法resultpjp.proceed();// 返回后逻辑longcostSystem.currentTimeMillis()-start;System.out.println(执行成功methodName耗时costms);returnresult;}catch(Throwablee){// 异常逻辑longcostSystem.currentTimeMillis()-start;System.out.println(执行失败methodName耗时costms);throwe;}finally{// 最终逻辑System.out.println(执行结束methodName);}}特点可以控制目标方法是否执行不调用proceed()则不执行。可以修改方法参数传入新的参数给proceed()。可以修改返回值返回不同的对象。可以处理异常吞掉或转换。必须返回Object且要返回proceed()的结果除非有意修改。方法参数是ProceedingJoinPoint不是JoinPoint。修改参数Around(serviceLayer())publicObjectaround(ProceedingJoinPointpjp)throwsThrowable{Object[]argspjp.getArgs();if(args.length0args[0]instanceofString){args[0]((String)args[0]).trim();// 修改参数}returnpjp.proceed(args);// 传入修改后的参数}修改返回值Around(serviceLayer())publicObjectaround(ProceedingJoinPointpjp)throwsThrowable{Objectresultpjp.proceed();if(resultinstanceofString){return((String)result).toUpperCase();// 修改返回值}returnresult;}吞掉异常Around(serviceLayer())publicObjectaround(ProceedingJoinPointpjp)throwsThrowable{try{returnpjp.proceed();}catch(Exceptione){System.out.println(异常被吞掉e.getMessage());returnnull;// 不再抛出异常}}适用场景性能监控统计耗时事务管理开启、提交、回滚缓存先查缓存未命中才执行重试失败后重试限流超过阈值则拒绝修改参数或返回值七、通知的执行顺序1. 单个切面中的执行顺序Around 前半部分 → Before → 目标方法执行 → AfterReturning正常返回或 AfterThrowing抛出异常 → After Around 后半部分完整示例AspectComponentpublicclassOrderAspect{Before(serviceLayer())publicvoidbefore(){System.out.println(1. Before);}AfterReturning(serviceLayer())publicvoidafterReturning(){System.out.println(3. AfterReturning);}After(serviceLayer())publicvoidafter(){System.out.println(4. After);}Around(serviceLayer())publicObjectaround(ProceedingJoinPointpjp)throwsThrowable{System.out.println(0. Around 前);Objectresultpjp.proceed();System.out.println(5. Around 后);returnresult;}}输出0. Around 前 1. Before 目标方法执行 3. AfterReturning 4. After 5. Around 后2. 多个切面的执行顺序用Order控制切面优先级数值越小优先级越高。AspectComponentOrder(1)publicclassLogAspect{Before(serviceLayer())publicvoidbefore(){System.out.println(LogAspect Before);}After(serviceLayer())publicvoidafter(){System.out.println(LogAspect After);}}AspectComponentOrder(2)publicclassTransactionAspect{Before(serviceLayer())publicvoidbefore(){System.out.println(TransactionAspect Before);}After(serviceLayer())publicvoidafter(){System.out.println(TransactionAspect After);}}输出LogAspect Before TransactionAspect Before 目标方法执行 TransactionAspect After LogAspect AfterBefore按Order从小到大执行After按Order从大到小执行。这就像栈结构先进后出。八、通知类型的对比总结对比维度BeforeAfterAfterReturningAfterThrowingAround执行时机方法前方法后总是正常返回后异常抛出后方法前后获取参数✅✅✅✅✅获取返回值❌❌✅❌✅获取异常❌❌❌✅✅阻止方法执行❌❌❌❌✅修改参数❌❌❌❌✅修改返回值❌❌❌❌✅处理异常❌❌❌❌✅参数类型JoinPointJoinPointJoinPointJoinPointProceedingJoinPoint九、如何选择通知类型需求推荐通知记录方法调用前日志Before记录方法调用后日志无论异常After记录返回值AfterReturning记录异常AfterThrowing统计方法耗时Around事务管理Around缓存处理Around修改参数Around修改返回值Around吞掉异常Around重试机制Around选择原则简单的日志记录用Before、After、AfterReturning。需要获取返回值用AfterReturning。需要获取异常用AfterThrowing。需要控制方法执行、修改参数/返回值、处理异常时用Around。Around虽然功能最全但代码最复杂容易出错。能用简单通知解决的不要用Around。十、常见问题1. Around 忘记返回 proceed() 的结果// ❌ 错误目标方法的返回值被丢弃Around(serviceLayer())publicObjectaround(ProceedingJoinPointpjp)throwsThrowable{pjp.proceed();returnnull;}// ✅ 正确Around(serviceLayer())publicObjectaround(ProceedingJoinPointpjp)throwsThrowable{returnpjp.proceed();}2. AfterReturning 的 returning 类型不匹配// 目标方法返回 void但通知声明了 String 类型AfterReturning(pointcutserviceLayer(),returningresult)publicvoidafterReturning(JoinPointjp,Stringresult){// 不会执行因为返回值类型不匹配}3. AfterThrowing 不能处理异常AfterThrowing只能记录异常不能吞掉异常。异常会继续向上抛出。如果需要处理异常必须用Around。4. 通知的执行顺序与预期不符多个切面时用Order明确指定顺序。Before按Order升序执行After按Order降序执行。5. Around 中调用 proceed() 多次proceed()可以调用多次实现重试机制。但要注意多次调用会多次执行目标方法。Around(serviceLayer())publicObjectretry(ProceedingJoinPointpjp)throwsThrowable{intmaxRetries3;for(inti0;imaxRetries;i){try{returnpjp.proceed();}catch(Exceptione){if(imaxRetries-1)throwe;System.out.println(重试第 (i1) 次);}}returnnull;}十一、总结通知一句话说明核心能力Before目标方法前执行前置准备、日志、校验After目标方法后执行总是资源清理、收尾日志AfterReturning正常返回后执行获取返回值、结果处理AfterThrowing异常抛出后执行获取异常、异常记录Around目标方法前后执行全控制参数、返回值、异常五种通知各有用途Before和After适合简单的日志和清理AfterReturning和AfterThrowing适合针对结果的处理Around功能最全但最复杂。理解每种通知的执行时机和能力边界就能在实际开发中选择最合适的通知类型写出清晰、健壮的切面代码。
返回列表