Spring AOP 通知执行顺序《二》同一切面相同类型通知

同一切面中的同一类型通知的执行顺序

        Spring 官方文档中是这样描述的:当在同一 @Aspect 类中定义的同一类型的两条通知(例如,两个@After 通知方法)都需要在同一连接点上运行时,顺序是不明确的(因为没有办法通过反射来获取javac编译类的源代码声明顺序)。考虑在每个 @Aspect 类中的每个连接点将这样的两个通知方法折叠成一个通知方法,或者将通知方法片段重构成单独的 @Aspect 类,您可以通过 Ordered 接口或 @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";
    }
}

定义切面和通知:

@Aspect
@Component
public class AopConfig {


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

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

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

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

}

注意最后一个通知方法名称是 “b()”。

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext
               = new AnnotationConfigApplicationContext(BootStrap.class);
        TestService testService = (TestService) applicationContext.getBean("testService");
        testService.test();
    }

启动程序,输出结果让人略感意外:

AopConfig Before4 advice exec ...
AopConfig Before1 advice exec ...
AopConfig Before2 advice exec ...
AopConfig Before3 advice exec ...
test() execute ...

运行多次,结果都是一样。似乎是跟通知方法名称字符串大小决定执行顺序。把TestService 改成了实现接口,让Spring 使用JDK动态代理的方式实现AOP,输出结果仍然是上述结果。而且,我们修改通知方法的名称,总会得到如下结论:通知方法名称字符串自然顺序小的优先执行。这似乎与官方文档所说的不一样:

When two pieces of the same type of advice (for example, two @After advice methods) defined in the same @Aspect class both need to run at the same join point, the ordering is undefined (since there is no way to retrieve the source code declaration order through reflection for javac-compiled classes). 

自己没有分析清楚原因,就去StackOverFlow上提出了这个问题。有人提出在多个JVM平台上跑一下,我在 HotSpot 和 OpenJ9 两种JVM上分别运行,结果一致。或许,在其他JVM上运行会出现不同结果,这个没有再测试。因为看到一个答案说的很有道理,这里引用一下:

If the documentation states that "ordering is undefined", it does not necessarily mean that it is random but that Spring AOP itself has no way of enforcing ordering. Besides, the result might change if there is no debug information in the class, the class is obfuscated, the JVM or compiler (e.g. Javac, ECJ, Groovyc) ist different or whatever. It means, you cannot rely on the ordering and doing so by applying your private reverse engineering approach could lead to your aspect code breaking in the future.”

这里的 undefined 说的是Spring没有办法指定这种情况下通知的执行顺序,但是它在特定的平台上运行可能是有特定的顺序的。我们不应该依赖这种针对平台的特定顺序,因为换一个平台编译,就有可能导致程序结果跟预期不一致。所以我们应该像Spring官方文档建议的那样,考虑在每个方面类中的每个连接点将这些通知方法合并为一个通知方法,在这个方法里指定代码逻辑顺序,或者将通知片段重构为单独的切面类,使用@Order注解或者Ordered 接口进行排序。

再次赞一下StackOverFlow,提出问题后,迅速就有人做出高质量的回答,真心赞!

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值