天天看点

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 ...