小結:各種通知的運行順序
這篇博客主要是來總結一下基於註解配置的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和基於註解的環繞通知,執行的結果沒有變化。