欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring框架之Day08多切面AOP执行顺序和AOP使用场景

程序员文章站 2022-05-24 23:44:21
...

环绕通知@Around执行顺序

环绕前置通知-》环绕正常执行通知-》环绕后置通知
出现异常后:环绕前置通知-》环绕异常通知-》环绕后置通知

如果存在四大通知混合情况,执行顺序为:
普通前置-》环绕前置-》环绕正常执行通知-》环绕后置通知-》普通后置-》普通正常执行通知
出现异常后:普通前置-》环绕前置-》环绕异常通知-》环绕后置-》普通后置-》!普通正常执行通知(实则异常它仍执行结果为null)

多切面执行顺序

无环绕

根据先进后出的原则(栈)谁最后接触核心目标方法,谁先执行完后置通知方法。
举例:
存在LogUtilsAOP和MultiAOP两个工具切面类,M后进入,先出来。即执行顺序为L前置通知-》M前置通知-》M最终结束-》M正常执行完-》L最终结束-》L正常执行完
【[LogUtilsAOP]div】方法开始执行,用的参数列表为【[4, 2]】
【[MultiAOP]div】方法开始执行,用的参数列表为【[4, 2]】
[MultiAOP]div()方法finally最终结束了
【[MultiAOP]div】方法正常执行完成,计算结果为:2
[LogUtilsAOP]div()方法finally最终结束了
【[LogUtilsAOP]div】方法正常执行完成,计算结果为:2

有环绕

环绕单独在外

三个类(单独环绕工具切面类):LogUtilsAOP(1)、MultiAOP(2)、LogUtilsAroundAOP(3)
每个类分别使用注解指定顺序@Order(1) @Order(2) @Order(3)
数字越小越先进入,最后才执行完出去。即执行顺序为:L前置-》M前置-》环绕前置-》环绕正常执行完-》环绕后置-》M后置最终执行-》M->正常执行完-》L后置最终执行-》L正常执行完

【[LogUtilsAOP]div】方法开始执行,用的参数列表为【[4, 2]】
【[MultiAOP]div】方法开始执行,用的参数列表为【[4, 2]】
【环绕前置通知】div()方法开始执行
【环绕正常执行通知】div()方法返回,计算结果返回值为2
【环绕后置通知】div()方法最终结束
[MultiAOP]div()方法finally最终结束了
【[MultiAOP]div】方法正常执行完成,计算结果为:2
[LogUtilsAOP]div()方法finally最终结束了
【[LogUtilsAOP]div】方法正常执行完成,计算结果为:2

@Aspect
@Component
@Order(1)
public class LogUtilsAOP {
    @Pointcut("execution(public int com.lwt.Calculator.impl.MyMathCal.*(int,int))")
    public void myexecution(){
    }
    /**
     * 想在执行目标方法之前运行@Before,在那个方法之前运行呢?写切入点表达式("myexecutioncution(public int 方法全描述)")
     */
//    @Before("myexecution(public int com.lwt.Calculator.impl.MyMathCal.*(int,int))")
    @Before("myexecution()")
    public void logStart(JoinPoint joinPoint) { // 参数列表不可乱写
//        System.out.println("[xxx]方法开始,它使用的参数是"+ Arrays.asList(objects));
//        System.out.println("【"+method.getName()+"】方法开始执行,用的参数列表为【"+ Arrays.asList(objects) +"】");
        System.out.println("【[LogUtilsAOP]"+joinPoint.getSignature().getName()+"】方法开始执行,用的参数列表为【"+Arrays.asList(joinPoint.getArgs())+"】");
    }

    /**
     * 想在目标方法正常执行完成之后执行@AfterReturning
     */
    @AfterReturning(value = "myexecution()",returning = "result")
    public void logReturn(JoinPoint joinPoint,Object result) {
//        System.out.println("【"+method.getName()+"】方法正常执行完成,计算结果为:"+Arrays.asList(objects));
        System.out.println("【[LogUtilsAOP]"+joinPoint.getSignature().getName()+"】方法正常执行完成,计算结果为:"+result);
    }

    /**
     * 想在目标方法出现异常之后执行@AfterThrowing
     */
    @AfterThrowing(value = "myexecution()",throwing = "e")
    public void logException(JoinPoint joinPoint,Exception e) {
//        System.out.println(method.getName()+"()方法执行出现异常,异常信息是"+e.getCause());
        System.out.println("[LogUtilsAOP]"+joinPoint.getSignature().getName()+"()方法执行出现异常,异常信息是"+e);
    }

    /**
     * 想在方法结束后执行@After方法结束后
     */
    @After("execution(public int com.lwt.Calculator.impl.MyMathCal.*(int,int))")
    public void logFinally(JoinPoint joinPoint){
        System.out.println("[LogUtilsAOP]"+joinPoint.getSignature().getName()+"()方法finally最终结束了");
    }
}
/**
 * L M N O P Q R S T U V W X Y Z
 * @Author Li Weitong
 * @Date 2020/12/10 10:34
 */
@Aspect
@Component
@Order(2) // 数值越小,优先级越高,切面的执行顺序

public class MultiAOP {
    // 跨类引入全名切入点表达式抽取出的方法
    @Before("com.lwt.Calculator.utils.LogUtilsAOP.myexecution()")
    public static void logStart(JoinPoint joinPoint){
        System.out.println("【[MultiAOP]"+joinPoint.getSignature().getName()+"】方法开始执行,用的参数列表为【"+ Arrays.asList(joinPoint.getArgs()) +"】");
    }

    @AfterReturning(value = "com.lwt.Calculator.utils.LogUtilsAOP.myexecution()",returning = "result")
    public static  void logReturn(JoinPoint joinPoint,Object result){
        System.out.println("【[MultiAOP]"+joinPoint.getSignature().getName()+"】方法正常执行完成,计算结果为:"+result);
    }

    @AfterThrowing(value = "com.lwt.Calculator.utils.LogUtilsAOP.myexecution()",throwing = "e")
    public static void logException(JoinPoint joinPoint,Exception e){
        System.out.println("[MultiAOP]"+joinPoint.getSignature().getName()+"()方法执行出现异常,异常信息是"+e);
    }

    @After(value = "com.lwt.Calculator.utils.LogUtilsAOP.myexecution()")
    public static void logFinally(JoinPoint joinPoint){
        System.out.println("[MultiAOP]"+joinPoint.getSignature().getName()+"()方法finally最终结束了");

    }
}
/**
 * L M N O P Q R S T U V W X Y Z
 * @Author Li Weitong
 * @Date 2020/12/9 17:39
 */
@Aspect
@Component
@Order(3)
public class LogUtilsAroundAOP {
    @Pointcut("execution(public int com.lwt.Calculator.impl.MyMathCal.*(int,int))")
    public void myexecution(){
    }

    /*环绕通知4合1
            环绕通知中有一个参数ProceedingJoinPoint pjp
        */
    @Around("myexecution()")
    public Object myAround(ProceedingJoinPoint pjp){
        Object[] args = pjp.getArgs();
        String name = pjp.getSignature().getName();

        Object proceed = null;
        try {
            System.out.println("【环绕前置通知】"+name+"()方法开始执行");// @Before环绕前置通知
            proceed = pjp.proceed(args); // 利用反射调用目标方法即可,等同于method.invoke(obj,args)
            System.out.println("【环绕正常执行通知】"+name+"()方法返回,计算结果返回值为"+proceed);// @AfterReturning环绕正常执行返回通知
        } catch (Throwable throwable) {
            System.out.println("【环绕异常通知"+name+"()方法出现异常,异常信息:】"+throwable);// @AfterThrowing环绕异常通知
            throwable.printStackTrace();
        } finally {
            System.out.println("【环绕后置通知】"+name+"()方法最终结束");// @After环绕后置通知
        }
        return proceed;// 反射调用的返回值返回出去
    }
}

测试:

/*
    无接口
     */
    @Test
    public void test2(){
        ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-configAOP1.xml");
        // 从ioc容器中拿到目标对象,注意:如果想要用类型,一定用他的接口类型,不要用本类。但是,当不实现接口后,则用假神被代理类或通过默认首字母小写id来获取bean对象
        MyMathCal bean1 = ioc.getBean(MyMathCal.class);
        bean1.div(4,2);

        System.out.println(bean1); // [email protected]
        // ioc容器中保存的组件是代理对象class com.lwt.Calculator.impl.MyMathCal$$EnhancerBySpringCGLIB$$9b6eec87
        System.out.println("ioc容器中保存的组件是代理对象"+bean1.getClass());

        Object bean2 = ioc.getBean("myMathCal");
        System.out.println(bean2);
        System.out.println("ioc容器中保存的组件是代理对象"+bean2.getClass());
    }

环绕在四大通知内

环绕优先于当前类的四大通知先进入执行,最后一个执行结束退出。