小结:各种通知的运行顺序

小结:各种通知的运行顺序

这篇博客主要是来总结一下基于注解配置的AOP,通知的执行顺序会有一些小问题。

AOP是通过动态代理实现的,它的大致过程如下:

	advicer.before();
	try{
		target.foo();
		advicer.afterReturning();
	}catch(Exception e){
		advicer.afterThrowing();
		throw new RuntimeException(e);
	}finally{
		advicer.after();
	}

前置通知和最终通知一定会执行,后置通知和异常通知只有一个会执行

所以,运行的顺序分为两种情况:

正常结束

  1. 前置通知
  2. 切入点方法
  3. 后置通知
  4. 最终通知

异常结束

  1. 前置通知
  2. 切入点方法
  3. 异常通知
  4. 最终通知

验证XML配置的AOP的运行顺序

/**
 * 通知类
 */
@Component
public class Advicer {
    public void beforAdvice(){
        System.out.println("前置通知");
    }

    public void afterReturning(){
        System.out.println("后置通知");
    }

    public void afterThrowing(){
        System.out.println("异常通知");
    }

    public void after(){
        System.out.println("最终通知");
    }
}

/**
 * 目标(被代理)
 */
@Component
public class Target {
    public void foo(){
        System.out.println("Target.foo()");
    }
}
    <aop:config>
        <aop:aspect id="aspect" ref="advicer">
            <aop:pointcut id="pointcut" expression="execution(* aop.test.basedAnno.Target.foo())"/>
            <aop:before method="beforAdvice" pointcut-ref="pointcut"></aop:before>
            <aop:after-returning method="afterReturning" pointcut-ref="pointcut"></aop:after-returning>
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut"></aop:after-throwing>
            <aop:after method="after" pointcut-ref="pointcut"></aop:after>
        </aop:aspect>
        
    </aop:config>

  public void runTarget(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Target t = (Target) context.getBean("target");
        t.foo();
    }

运行结果:
(正常退出)
在这里插入图片描述

(异常退出)
在这里插入图片描述

可以看到,和前面的预计顺序是相同的

验证注解配置的AOP的运行顺序

@Component
@Aspect
public class Advicer {

    @Pointcut("execution(* aop.test.basedAnno.Target.foo(..))")
    private void pointcutFoo(){}

    @Before("pointcutFoo()")
    public void beforAdvice(){
        System.out.println("前置通知");
    }

    @AfterReturning("pointcutFoo()")
    public void afterReturning(){
        System.out.println("后置通知");
    }

    @AfterThrowing("pointcutFoo()")
    public void afterThrowing(){
        System.out.println("异常通知");
    }

    @After("pointcutFoo()")
    public void after(){
        System.out.println("最终通知");
    }
}


运行结果:
(正常退出)
在这里插入图片描述

(异常退出)
在这里插入图片描述

可以看到,这里的顺序除了一些问题,最终通知会提前到后置通知/异常通知之前执行

关于环绕通知

环绕通知是将切入点方法替换为另一个方法,所以关于各种通知的执行顺序,由编码实现,所以基于XML和基于注解的环绕通知,执行的结果没有变化。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章