若不想看演示,请直接撸到最后(锚点怎么设

各种通知

  • 前置通知(Before):在目标方法执行之前执行
  • 异常通知(AfterThrowing):当目标方法执行过程中出现异常时执行
  • 后置通知(AfterReturning):当目标方法能正常结束后执行
  • 最终通知(After):无论目标方法能否正常结束都会执行
  • 环绕通知(Around):伴随目标方法各个时期(前面四种通知的综合)

执行顺序

一. 目标方法无异常时

1. 整一个目标方法(切面)就以某Controller方法为例吧

@RestController
public class TestAdvice {

    @RequestMapping("/test/advice")
    public String test(){
        System.out.println("Controller.test()正在干活...");
        return "yes";
    }
}

2. 写一个aop类,造一堆通知

@Component
@Aspect
public class MyAdvice {

    private final 
    String execution = "execution(public String controller.TestAdvice.test())";

    @Before(execution)
    public void before(){
        System.out.println("Before通知执行了");
    }

    @AfterReturning(value = execution, returning = "value")
    public String afterReturning(String value){
        System.out.println("AfterReturning通知执行了");
        //原封不动的返回出去
        return value;
    }

    @AfterThrowing(value = execution, throwing = "th")
    public void afterThrowing(Throwable th){
        System.out.println("AfterThrowing通知执行了," + th.getMessage());
        
        //当目标方法出现未捕获的异常时,不知道先触发本方法,还是先触发Around里的catch块
        //所以再抛一个异常,如果先触发本方法,那Around的catch块里应该打印下面这句话
        throw new RuntimeException("来自AfterThrowing的异常");
    }

    @After(execution)
    public void after(){
        System.out.println("After通知执行了");
    }

    @Around(execution)
    public String around(ProceedingJoinPoint pjp){
        try{
            System.out.println("Around.before执行了");
            pjp.proceed();
            System.out.println("目标方法已执行完毕");
            return "yes";
        }catch (Throwable throwable) {
            System.out.println("Around.afterThrowing执行了, " + throwable.getMessage());
        } finally {
            System.out.println("Around.after执行了");
        }
        return "no";
    }
}

3.完活,浏览器访问

spring方法执行耗时 spring执行顺序_spring

4.看控制台

spring方法执行耗时 spring执行顺序_java_02

5.结论

Around.before  >  Before  >  Around.after  >  After  >  AfterReturning

再废两句话

二. 目标方法有异常时

在上面的 Controller.test() 添加一条测试异常全国通用语句

@RestController
public class TestAdvice {

    @RequestMapping("/test/advice")
    public String test(){
        System.out.println("Controller.test()正在干活...");
        int i = 1 / 0; //往这看
        return "yes";
    }
}

浏览器访问

spring方法执行耗时 spring执行顺序_aop_03

看控制台

spring方法执行耗时 spring执行顺序_System_04

结论

Around.afterThrowing  >  AfterThrowing

Around通知就是这么横!它抢先AfterThrowing一步,直接把异常处理了,然后就没AfterThrowing啥事儿了。顺理成章的,当异常被消灭后,AfterReturning也蹦了出来

如果上面的结论是对的,那把Around里的catch注释掉,结果应该是AfterThrowing会执行

@Around(execution)
public String around(ProceedingJoinPoint pjp) throws Throwable {
    try{
        System.out.println("Around.before执行了");
        pjp.proceed();
        System.out.println("目标方法已执行完毕");
        return "yes";
    }
    /*catch (Throwable throwable) {
        System.out.println("Around.afterThrowing执行了, " + throwable.getMessage());
    } */
    finally {
        System.out.println("Around.after执行了");
    }
}

可以看到,AfterThrowing确实冒出来了,证明上面的结论是对的

spring方法执行耗时 spring执行顺序_aop_05


而且这次AfterReturning并没有出现。

注意:AfterThrowing方法只是当目标方法存在未捕获的异常时被动执行,人家压根就不负责catch;而浏览器显示的异常是被AfterThrowing方法内我们手动抛出的异常覆盖了

spring方法执行耗时 spring执行顺序_spring_06

三. 总结

  • Around是老大,优先级最高
    Around.before 先于 Before
    Around.after 先于 After
    Around.afterThrowing 先于 AfterThrowing
  • Before > After > AfterThrowing