當一個連接點同時織入多個增強時,就存在順序調用問題:
1. 增強在同一個切面類中定義:依照增強在切面類中定義的順序依次織入。
2. 增強位於不同的切面,但果這些切面都實現了org.springframework.core.Ordered
接口,則由接口注解的順序號決定,順序號越小,對於不同的增強,織入順序為:
1. 前置增強->越先織入
2. 後置增強->越後織入
3. 最終增強->越後織入
4. 環繞增強->調用原方法之前的部分先織入,調用原方法之後的部分後織入
我們先來看一個實例:
package test.aop2;
public class UserController {
public void login(String name){
System.out.println("I'm "+name+" ,I'm logining");
}
}
/*-----------------------有序切面類1--------------------------*/
@Aspect
public class Annotation_aspect implements Ordered {//實現順序接口
@Pointcut("target(test.aop2.UserController)")
private void pointcut(){}//定義切點
@AfterThrowing("pointcut()")//後置增強
public void AfterThrowing() throws Throwable{
System.out.println("Annotation_aspect 實施AfterThrowing,優先級為" + getOrder());
}
@After("pointcut()")//最終增強
public void after() throws Throwable{
System.out.println("Annotation_aspect 實施after,優先級為" + getOrder());
}
@Before("pointcut()")//前置增強
public void before() throws Throwable{
System.out.println("Annotation_aspect 實施@before,優先級為" + getOrder());
}
@Around("pointcut()")//環繞增強
public void around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("Annotation_aspect 實施around前,優先級為" + getOrder());
joinPoint.proceed();
System.out.println("Annotation_aspect 實施around後,優先級為" + getOrder());
}
@Override
public int getOrder() {
return 1;
}
}
/*-----------------------有序切面類2--------------------------*/
@Aspect
public class Annotation_aspect2 implements Ordered {
@Pointcut("target(test.aop2.UserController)")
private void pointcut(){}
@AfterThrowing("pointcut()")
public void AfterThrowing() throws Throwable{
System.out.println("Annotation_aspect2 實施AfterThrowing,優先級為" + getOrder());
}
@After("pointcut()")
public void after() throws Throwable{
System.out.println("Annotation_aspect2 實施after,優先級為" + getOrder());
}
@Before("pointcut()")
public void before() throws Throwable{
System.out.println("Annotation_aspect2 實施@before,優先級為" + getOrder());
}
@Around("pointcut()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("Annotation_aspect2 實施around前,優先級為" + getOrder());
joinPoint.proceed();
System.out.println("Annotation_aspect2 實施around後,優先級為" + getOrder());
}
@Override
public int getOrder() {
return 2;
}
}
/*-----------------------無序切面類1--------------------------*/
@Aspect
public class noOrder_aspect {
@Pointcut("target(test.aop2.UserController)")
private void pointcut(){}
@AfterThrowing("pointcut()")
public void AfterThrowing() throws Throwable{
System.out.println("noOrder_aspect 實施AfterThrowing,無優先級");
}
@After("pointcut()")
public void after() throws Throwable{
System.out.println("noOrder_aspect 實施after,無優先級");
}
@Before("pointcut()")
public void before() throws Throwable{
System.out.println("noOrder_aspect 實施@before,無優先級");
}
@Around("pointcut()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("noOrder_aspect 實施around前,無優先級");
joinPoint.proceed();
System.out.println("noOrder_aspect 實施around後,無優先級");
}
}
/*-----------------------無序切面類2--------------------------*/
@Aspect
public class noOrder_aspect2 {
@Pointcut("target(test.aop2.UserController)")
private void pointcut(){}
@AfterThrowing("pointcut()")
public void AfterThrowing() throws Throwable{
System.out.println("noOrder_aspect2 實施AfterThrowing,無優先級");
}
@After("pointcut()")
public void after() throws Throwable{
System.out.println("noOrder_aspect2 實施after,無優先級");
}
@Before("pointcut()")
public void before() throws Throwable{
System.out.println("noOrder_aspect2 實施@before,無優先級");
}
@Around("pointcut()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("noOrder_aspect1 實施around前,無優先級");
joinPoint.proceed();
System.out.println("noOrder_aspect1 實施around後,無優先級");
}
}
public static void main(String args[]) throws Exception{
ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:test/aop2/aop.xml");
UserController userController = (UserController) ac.getBean("userController");
User user = new User();
userController.login("zenghao");
}
運行測試方法,控制台輸出:
Annotation_aspect 實施around前,優先級為1
Annotation_aspect 實施@before,優先級為1
Annotation_aspect2 實施around前,優先級為2
Annotation_aspect2 實施@before,優先級為2
noOrder_aspect 實施around前,無優先級
noOrder_aspect 實施@before,無優先級
noOrder_aspect1 實施around前,無優先級
noOrder_aspect2 實施@before,無優先級
I’m zenghao ,I’m logining
noOrder_aspect1 實施around後,無優先級
noOrder_aspect2 實施after,無優先級
noOrder_aspect2 實施AfterReturning,無優先級
noOrder_aspect 實施around後,無優先級
noOrder_aspect 實施after,無優先級
noOrder_aspect 實施AfterReturning,無優先級
Annotation_aspect2 實施around後,優先級為2
Annotation_aspect2 實施after,優先級為2
Annotation_aspect2 實施AfterReturning,優先級為2
Annotation_aspect 實施around後,優先級為1
Annotation_aspect 實施after,優先級為1
Annotation_aspect 實施AfterReturning,優先級為1針對以上信息,我們再復述前面結論來對比看,是完全符合的,順序號越小,對於不同的增強,織入順序為:
1. 前置增強->越先織入
2. 後置增強->越後織入
3. 最終增強->越後織入
4. 環繞增強->調用原方法之前的部分先織入,調用原方法之後的部分後織入認真觀察,我們還會發現:
1. 同一切面中,不同增強的織入順序為:環繞增強前半部分->前置增強->元目標函數調用->環繞增強後半部分->最終增強->後置增強。
2. 沒有實現order接口的“優先級”總是低於實現了order接口的,如針對前置增強來看,順序為:
Annotation_aspect > Annotation_aspect2 > noOrder_aspect > noOrder_aspect
3. 先觀察無序接口,我們會發現noOrder_aspect的優先級總是高於noOrder_aspect2的優先級,這是有前面IOC容器中前面Bean的配置順序決定的:
那麼,對於同樣實現了order接口的切面,它們是否也滿足這個規律呢?我們把Annotation_aspect2的優先級也設為1,然後運行測試函數,打印信息如下:
Annotation_aspect2 實施around前,優先級為1
Annotation_aspect2 實施@before,優先級為1
Annotation_aspect 實施around前,優先級為1
Annotation_aspect 實施@before,優先級為1
noOrder_aspect 實施around前,無優先級
noOrder_aspect 實施@before,無優先級
noOrder_aspect1 實施around前,無優先級
noOrder_aspect2 實施@before,無優先級
I’m zenghao ,I’m logining
noOrder_aspect1 實施around後,無優先級
noOrder_aspect2 實施after,無優先級
noOrder_aspect2 實施AfterReturning,無優先級
noOrder_aspect 實施around後,無優先級
noOrder_aspect 實施after,無優先級
noOrder_aspect 實施AfterReturning,無優先級
Annotation_aspect 實施around後,優先級為1
Annotation_aspect 實施after,優先級為1
Annotation_aspect 實施AfterReturning,優先級為1
Annotation_aspect2 實施around後,優先級為1
Annotation_aspect2 實施after,優先級為1
Annotation_aspect2 實施AfterReturning,優先級為1關注粗體部分,是不是優先級和IOC容器的配置順序對應上了,感興趣的同學可以把兩個沒有實現order接口的配置順序也交換一下,觀察它們的織入優先級是否也跟著變化了。
除了實現org.springframework.core.Ordered配置順序外,我們還可以使用注解@org.springframework.core.annotation.Order(優先級整數)來實現同樣的效果。在這裡,如果既使用注解,又使用接口實現的,且它們的優先級一致時,一樣是看IOC容器的bean順序,如:
其中Annotation_aspect3和Annotation_aspect4的類定義頭部為:
@Aspect
@Order(1)
public class Annotation_aspect3{
....
}
@Aspect
@Order(2)
public class Annotation_aspect4{
....
}
注釋再次運行測試程序,得到打印信息:
Annotation_aspect3 實施around前,注解優先級為1
Annotation_aspect3 實施@before,注解優先級為1
Annotation_aspect 實施around前,優先級為1
Annotation_aspect 實施@before,優先級為1
Annotation_aspect4 實施around前,注解優先級為2
Annotation_aspect4 實施@before,注解優先級為2
Annotation_aspect2 實施around前,優先級為2
Annotation_aspect2 實施@before,優先級為2
I’m zenghao ,I’m logining
Annotation_aspect2 實施around後,優先級為2
Annotation_aspect2 實施after,優先級為2
Annotation_aspect2 實施AfterReturning,優先級為2
Annotation_aspect4 實施around後,注解優先級為2
Annotation_aspect4 實施after,注解優先級為2
Annotation_aspect4 實施AfterReturning,注解優先級為2
Annotation_aspect 實施around後,優先級為1
Annotation_aspect 實施after,優先級為1
Annotation_aspect 實施AfterReturning,優先級為1
Annotation_aspect3 實施around後,注解優先級為1
Annotation_aspect3 實施after,注解優先級為1
Annotation_aspect3 實施AfterReturning,注解優先級為1
觀察IOC容器的配置順序,這裡也是對應上的。
綜上,我們得到結論:多個同一類型的增強(如都是前置增強)對同一連接點的織入順序是:配置有優先級看誰優先級高,優先級相同則看誰先在IOC容器注冊,配置了order接口或注釋的增強優先級總是高於沒配置的。
通過以上分析,我們能夠較好地總結出我們常用增強對應各種情況的織入順序。在了解這些織入順序後,有助於我們更靈活地將AOP運用到我們的項目中。