(III)AOP:第八节:多切面运行顺序

一、多切面的执行顺序

  1、切面中只有普通通知

    BValidateAspect切面:

@Aspect
@Component
public class BValidataAspect {



    @Before("com.njf.aop.utils.LogUtils.myPoint()")
    public static void logStart(JoinPoint joinpoint){
        Object[] args = joinpoint.getArgs();
        String methodName = joinpoint.getSignature().getName();
        System.out.println("[Validata前置通知]【"+ methodName +"】方法执行了,参数为【"+ Arrays.toString(args) +"】");
    }

    @AfterReturning(value = "com.njf.aop.utils.LogUtils.myPoint()", returning = "result")
    public static void logReturn(JoinPoint joinpoint, Object result){
        String methodName = joinpoint.getSignature().getName();
        System.out.println("[Validata返回通知]【"+ methodName +"】方法执行完成,他的结果为是:" + result);
    }

    @AfterThrowing(value = "com.njf.aop.utils.LogUtils.myPoint()", throwing = "exception")
    public static void logException(JoinPoint joinpoint, Exception exception){
        String methodName = joinpoint.getSignature().getName();
        System.out.println("[Validata异常通知]【"+ methodName +"】方法出现了异常,异常为: " + exception.getCause());
    }

    @After(value = "com.njf.aop.utils.LogUtils.myPoint()")
    public static void logEnd(JoinPoint joinpoint){
        String methodName = joinpoint.getSignature().getName();
        System.out.println("[Validata后置通知]【"+ methodName +"】方法执行最终完成");
    }

}

    LogUtils 切面:

@Aspect
@Component
public class LogUtils {

    @Pointcut("execution(public int com.njf.aop.calc.MyMathCalculator.*(int, int))")
    public void myPoint(){}

    @Before("execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))")
    public static void logStart(JoinPoint joinpoint){
        Object[] args = joinpoint.getArgs();
        String methodName = joinpoint.getSignature().getName();
        System.out.println("[LogUtils前置通知]【"+ methodName +"】方法执行了,参数为【"+ Arrays.toString(args) +"】");
    }

    //想在目标方法正执行完毕之后
    @AfterReturning(value = "execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))", returning = "result")
    public static void logReturn(JoinPoint joinpoint, Object result){
        String methodName = joinpoint.getSignature().getName();
        System.out.println("[LogUtils返回通知]【"+ methodName +"】方法执行完成,他的结果为是:" + result);
    }

    @AfterThrowing(value = "execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))", throwing = "exception")
    //想在目标方法出现异常时执行
    public static void logException(JoinPoint joinpoint, Exception exception){
        String methodName = joinpoint.getSignature().getName();
        System.out.println("[LogUtils异常通知]【"+ methodName +"】方法出现了异常,异常为: " + exception.getCause());
    }

    //想在目标方法结束时执行
    @After("execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))")
    public static void logEnd(JoinPoint joinpoint){
        String methodName = joinpoint.getSignature().getName();
        System.out.println("[LogUtils后置通知]【"+ methodName +"】方法执行最终完成");
    }

    测试运行:

    

     可以发现是 ValidataAspect 切面先执行前置,然后 LogUtils 前置,然后继续 LogUtils 里面的后置与返回,最后是 ValidataAspect 的后置与返回。

     图解:

      

    注意:切面的顺序是按照类名的字母顺序来执行的。

  2、使用注解指定切面运行顺序

  (1)在同一个连接点上应用不止一个切面时,除非明确指定,否则它们的优先级是不确定的;
  (2)切面的优先级可以通过实现 Ordered 接口或利用 @Order 注解指定;
  (3)实现 Ordered 接口,getOrder() 方法的返回值越小,优先级越高;
  (4)若使用 @Order 注解,序号出现在注解中
  (5)@Order(1),定义切面作用的优先级,值越小优先级越高,默认值为int的最大值
    案例:
@Component  
@Aspect
@Order(0)          //该切面先执行,序号越小,越先执行
public class TestAspect {
     
     @Before(value = "execution(public int  com.spring.aop.ICalc.add(int, int))")
     public void beforeMethod(JoinPoint joinPoint) {
         System.out.println("方法之前之前======");
     }
     
}

@Component   
@Aspect  
@Order(1)        //该切面后执行
public class LoggerAspect {
     @Before(value = "execution(public int  com.spring.aop.ICalc.add(int, int))")
     public void beforeMethod(JoinPoint joinPoint) {
         System.out.println("方法执行之前");
         Object[] args = joinPoint.getArgs();   //获取方法的参数
         String methodName =  joinPoint.getSignature().getName();  //获取方法名
         System.out.println("方法名:" + methodName +  ",参数:" + Arrays.toString(args));
     }
}

  3、加入环绕通知

    在 LogUtils 类中添加环绕通知,并且添加 @Order注解指定运行顺序

    LogUtils类:

@Aspect
@Component
@Order(1)
public class LogUtils {

    @Pointcut("execution(public int com.njf.aop.calc.MyMathCalculator.*(int, int))")
    public void myPoint(){}
    
    @Before("execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))")
    public static void logStart(JoinPoint joinpoint){
        Object[] args = joinpoint.getArgs();
        String methodName = joinpoint.getSignature().getName();
        System.out.println("[LogUtils前置通知]【"+ methodName +"】方法执行了,参数为【"+ Arrays.toString(args) +"】");
    }

    //想在目标方法正执行完毕之后
    @AfterReturning(value = "execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))", returning = "result")
    public static void logReturn(JoinPoint joinpoint, Object result){
        String methodName = joinpoint.getSignature().getName();
        System.out.println("[LogUtils返回通知]【"+ methodName +"】方法执行完成,他的结果为是:" + result);
    }

    @AfterThrowing(value = "execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))", throwing = "exception")
    //想在目标方法出现异常时执行
    public static void logException(JoinPoint joinpoint, Exception exception){
        String methodName = joinpoint.getSignature().getName();
        System.out.println("[LogUtils异常通知]【"+ methodName +"】方法出现了异常,异常为: " + exception.getCause());
    }

    //想在目标方法结束时执行
    @After("execution(public int com.njf.aop.calc.MyMathCalculator.add(int, int))")
    public static void logEnd(JoinPoint joinpoint){
        String methodName = joinpoint.getSignature().getName();
        System.out.println("[LogUtils后置通知]【"+ methodName +"】方法执行最终完成");
    }
    @Around("myPoint()")
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable {

        String methodName = pjp.getSignature().getName();
        Object[] args = pjp.getArgs();
        Object proceed = null;
        try {
            //@Before
            System.out.println("【环绕前置通知】---"+ methodName +"---【方法开始】");
            //就是利用反射调用目标方法即可,就是 method.invoke(obj, args)
            proceed = pjp.proceed(args);
            //@AfterReturning
            System.out.println("【环绕返回通知】---"+ methodName +"---【方法返回,返回值 "+ proceed +"】");
        } catch (Exception e) {
            //@AfterThrowing
            System.out.println("【环绕异常通知】---"+ methodName +"---【方法出现异常】,异常信息:" + e);
            //为了让外界能知道这个异常,这个异常一定要抛出去
            throw new RuntimeException(e);
        } finally {
            //@After
            System.out.println("【环绕后置通知】---"+ methodName +"---【方法结束】");
        }


        //反射调用后的返回值也一定返回出去,不返回会空指针
        return proceed;
    }
}

    BValidataAspect 类:

@Aspect
@Component
@Order(2)
public class BValidataAspect {


    @Before("com.njf.aop.utils.LogUtils.myPoint()")
    public static void logStart(JoinPoint joinpoint){
        Object[] args = joinpoint.getArgs();
        String methodName = joinpoint.getSignature().getName();
        System.out.println("[Validata前置通知]【"+ methodName +"】方法执行了,参数为【"+ Arrays.toString(args) +"】");
    }

    @AfterReturning(value = "com.njf.aop.utils.LogUtils.myPoint()", returning = "result")
    public static void logReturn(JoinPoint joinpoint, Object result){
        String methodName = joinpoint.getSignature().getName();
        System.out.println("[Validata返回通知]【"+ methodName +"】方法执行完成,他的结果为是:" + result);
    }

    @AfterThrowing(value = "com.njf.aop.utils.LogUtils.myPoint()", throwing = "exception")
    public static void logException(JoinPoint joinpoint, Exception exception){
        String methodName = joinpoint.getSignature().getName();
        System.out.println("[Validata异常通知]【"+ methodName +"】方法出现了异常,异常为: " + exception.getCause());
    }

    @After(value = "com.njf.aop.utils.LogUtils.myPoint()")
    public static void logEnd(JoinPoint joinpoint){
        String methodName = joinpoint.getSignature().getName();
        System.out.println("[Validata后置通知]【"+ methodName +"】方法执行最终完成");
    }

}

    测试运行:

    

    LogUtils 类中加入了环绕通知,不会影响 BValidataAspect,环绕只是影响当前切面

    图解:

      

    先执行各切面的前置,再执行目标方法,根据从内到外的顺序执行,(环绕只影响添加它的切面,且他在添加它的前面中优先级高)

原文地址:https://www.cnblogs.com/niujifei/p/15452020.html