小结:各种通知的运行顺序
这篇博客主要是来总结一下基于注解配置的AOP,通知的执行顺序会有一些小问题。
AOP是通过动态代理实现的,它的大致过程如下:
advicer.before();
try{
target.foo();
advicer.afterReturning();
}catch(Exception e){
advicer.afterThrowing();
throw new RuntimeException(e);
}finally{
advicer.after();
}
前置通知和最终通知一定会执行,后置通知和异常通知只有一个会执行
所以,运行的顺序分为两种情况:
正常结束
- 前置通知
- 切入点方法
- 后置通知
- 最终通知
异常结束
- 前置通知
- 切入点方法
- 异常通知
- 最终通知
验证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和基于注解的环绕通知,执行的结果没有变化。