天天看點

Spring AOP 通知執行順序《三》不同切面相同類型通知順序

同一切面中的同一類型通知的執行順序       

       Spring 官方文檔中是這樣描述的:當在不同切面定義的兩條相同類型通知都需要在同一連接配接點上運作時,除非另行指定,否則執行順序是不确定的。 您可以通過指定優先級來控制執行順序。 通過在切面類中實作 org.springframework.core.Ordered 接口或使用 @Order 注解對其進行注解。 給定兩個切面,從 Ordered.getValue()(或 @Order 注解值)傳回較低值的切面具有較高的優先級。

@Order 注解 和 Ordered 接口

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface Order {

	/**
	 * The order value.
	 * <p>Default is {@link Ordered#LOWEST_PRECEDENCE}.
	 * @see Ordered#getOrder()
	 */
	int value() default Ordered.LOWEST_PRECEDENCE;

}
           

        在 org.springframework.core.annotation.Order 注解注釋文檔中,可以知道 @Order 是可選的, 其 value 值和 org.springframework.core.Ordered 接口中 getOrder() 方法作用相同,用來指定優先級。 @Order 注解預設value值是最低優先級 Ordered.LOWEST_PRECEDENCE。它定義在 Ordered 接口中:

​public interface Ordered {

	/**
	 * Useful constant for the highest precedence value.
	 * @see java.lang.Integer#MIN_VALUE
	 */
	int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;

	/**
	 * Useful constant for the lowest precedence value.
	 * @see java.lang.Integer#MAX_VALUE
	 */
	int LOWEST_PRECEDENCE = Integer.MAX_VALUE;


	/**
	 * Get the order value of this object.
	 * <p>Higher values are interpreted as lower priority. As a consequence,
	 * the object with the lowest value has the highest priority (somewhat
	 * analogous to Servlet {@code load-on-startup} values).
	 * <p>Same order values will result in arbitrary sort positions for the
	 * affected objects.
	 * @return the order value
	 * @see #HIGHEST_PRECEDENCE
	 * @see #LOWEST_PRECEDENCE
	 */
	int getOrder();

}

​
           

       從中我們可以看到:優先級值越小,優先級越高。另外,需要注意的是 @Order 注解可以用在方法或者屬性字段上,但是在指定通知優先級時,隻能用在切面類上,用在通知方法上是無效的。

@Order 注解使用

采用Spring AOP 版本:

<dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.2.7.RELEASE</version>
    </dependency>
           

定義測試用service:

@Service
public class TestService {

    public String test(){
        System.out.println("test() execute ...");
        return "hello world";
    }
}
           

定義兩個切面 AopConfig 和 AopConfig2,我們使用環繞通知 @Around 類型通知:

@Aspect
@Component
public class AopConfig {

    @Around("execution (* com.dxc.opentalk.springtest.service.TestService.*())")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("AopConfig around advice begin ...");
        joinPoint.proceed();
        System.out.println("AopConfig around advice end ...");
    }

}
           
@Aspect
@Order(1)
@Component
public class AopConfig2 {

    @Around("execution (* com.dxc.opentalk.springtest.service.TestService.*())")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("AopConfig2 around advice begin ...");
        joinPoint.proceed();
        System.out.println("AopConfig2 around advice end ...");
    }
}
           

通知的執行順序為:

AopConfig2 around advice begin ...
AopConfig around advice begin ...
test() execute ...
AopConfig around advice end ...
AopConfig2 around advice end ...
           

AopConfig 預設是最低優先級, AopConfig2 中設定 @Order(1),按照值越小優先級越高,是以是 AopConfig2 中定義的 @Around 通知先進入連接配接點後退出連接配接點。 我們稍作修改,在 AopConfig 類上加上 @Order(0),

@Aspect
@Order(0)
@Component
public class AopConfig {

    @Around("execution (* com.dxc.opentalk.springtest.service.TestService.*())")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("AopConfig around advice begin ...");
        joinPoint.proceed();
        System.out.println("AopConfig around advice end ...");
    }

}
           

通知的執行順序即為:

AopConfig around advice begin ...
AopConfig2 around advice begin ...
test() execute ...
AopConfig2 around advice end ...
AopConfig around advice end ...
           

此時,AopConfig 切面的優先級高于 AopConfig2,是以輸出結果變成了 AopConfig 中的環繞通知包裹了 AopConfig2 中的環繞通知。

注意點

      這裡需要注意的就是各種類型通知在不同優先級下的表現,比如上面的環繞通知@Around是優先級高的包裹優先級低的。@Before 類型通知是優先級高的先執行,@After 類型通知是優先級高的後執行。

測試:

AopConfig 類,預設最低優先級:

@Aspect
@Component
public class AopConfig {

    @Before("execution (* com.dxc.opentalk.springtest.service.TestService.*())")
    public void before()  {
        System.out.println("AopConfig before advice begin ...");
    }

    @Around("execution (* com.dxc.opentalk.springtest.service.TestService.*())")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("AopConfig around advice begin ...");
        joinPoint.proceed();
        System.out.println("AopConfig around advice end ...");
    }

    @After("execution (* com.dxc.opentalk.springtest.service.TestService.*())")
    public void after()  {
        System.out.println("AopConfig after advice begin ...");
    }
}
           

AopConfig2 類:

@Aspect
@Order(2)
@Component
public class AopConfig2 {

    @Before("execution (* com.dxc.opentalk.springtest.service.TestService.*())")
    public void before()  {
        System.out.println("AopConfig2 before advice begin ...");
    }

    @Around("execution (* com.dxc.opentalk.springtest.service.TestService.*())")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("AopConfig2 around advice begin ...");
        joinPoint.proceed();
        System.out.println("AopConfig2 around advice end ...");
    }

    @After("execution (* com.dxc.opentalk.springtest.service.TestService.*())")
    public void after()  {
        System.out.println("AopConfig2 after advice begin ...");
    }
}
           

通知執行順序如下:

AopConfig2 around advice begin ...
AopConfig2 before advice begin ...
AopConfig around advice begin ...
AopConfig before advice begin ...
test() execute ...
AopConfig after advice begin ...
AopConfig around advice end ...
AopConfig2 after advice begin ...
AopConfig2 around advice end ...