基于AspectJ的Spring AOP Advice执行顺序

前言

用过Spring做过开发的同学,多少都对Spring的AOP有所了解和使用的经验.也都知道有@Around,@Before,@After等Advice.至于Spring AOP的基本概念,我想大家也都清楚,这里也就不再赘述.

今天在论坛里看到了一个问题,谈到了Spring AOP的Advice执行顺序的问题,看到问题以后,突然发现自己对这方面的理解也不是十分的深入.在回答问题的同时,正好对这个知识点深入的了解一下.

本文基于Spring AspectJ AOP的方式来进行描述.

Spring官方对Advice执行顺序的解释

参考文档:aop-ataspectj-advice-ordering

When two pieces of advice defined in different aspects both need to run at the same join point, unless you specify otherwise the order of execution is undefined. You can control the order of execution by specifying precedence. This is done in the normal Spring way by either implementing the org.springframework.core.Ordered interface in the aspect class or annotating it with the Order annotation. Given two aspects, the aspect returning the lower value from Ordered.getValue() (or the annotation value) has the higher precedence.

上面的内容简单的说就是,当对于同一个Join Point有两个Advice定义在不同的Aspect中的时候,他们的执行顺序是根据Aspect类的@Order注解的值,或者通过实现Order并重写getValue方法的值来决定的.同时,Order的值越小,优先级越高.

When two pieces of advice defined in the same aspect both need to run at the same join point, the ordering is undefined

当同一个Aspect中对同一个Join Point有两个Advice的话,这两个Advice的顺序是不固定的.

实例

首先我们建立一个Spring的工程,然后基于spring-test进行下面的操作.本文的spring版本是:4.3.11.RELEASE

1. 建立一个AuthAnnotation注解类,该注解作用在方法上即可

clipboard.png

2. 在spring的配置中添加aspect aop支持

<aop:aspectj-autoproxy/>

3. 编写Aspect的Advice,请注意图一红框的方法名

clipboard.png
clipboard.png

4. 编写一个Service,符合上面的切入点规则即可

clipboard.png

5. 执行单元测试,调用TestService.test()方法,输出结果如下

----Order1:checkAuth:Annotation----
----Order1:checkAuthPackage:Execution----
----Order2:checkAuthPackage:Execution----
---Service:Test---

多次运行以后,我们会发现一个问题,就是Order1的checkAuth方法一直是第一个执行.这是不是说明,以注解方式的PointCut是不是会有首先执行的优先级?
如果是的话,这就不符合上面Spring官方文档的说法了.来让我们看看为什么?

ReflectiveAspectJAdvisorFactory

该类的作用是基于AspectJ时,创建Spring AOP的Advice.

static {
        CompoundComparator<Method> comparator = new CompoundComparator<Method>();
        comparator.addComparator(new ConvertingComparator<Method, Annotation>(
                new InstanceComparator<Annotation>(
                        Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),
                new Converter<Method, Annotation>() {
                    @Override
                    public Annotation convert(Method method) {
                        AspectJAnnotation<?> annotation =
                                AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
                        return (annotation != null ? annotation.getAnnotation() : null);
                    }
                }));
        comparator.addComparator(new ConvertingComparator<Method, String>(
                new Converter<Method, String>() {
                    @Override
                    public String convert(Method method) {
                        return method.getName();
                    }
                }));
        METHOD_COMPARATOR = comparator;
    }

从该类的静态方法块中我们可以看到,Advice列表的添加顺序是按照Around/Before/After/AfterReturning/AfterThrowing的顺序,同时根据Advice的方法名顺序进行排序的.

clipboard.png

clipboard.png

当调用到getAdvisors方法的时候,会调用getAdvisorMethods方法,来获取所有的advice Method对象.同时根据METHOD_COMPARATOR的规则进行排序.

最后的测试

我们修改OrderOneAspect这个类中,checkAuthPackage方法的名字为aCheckAuthPackage,在执行一次单元测试的结果如下:

clipboard.png

----Order1:checkAuthPackage:Execution----
----Order1:checkAuth:Annotation----
----Order2:checkAuthPackage:Execution----
---Service:Test---

输出的结果中,我们可以看到,优先执行的不再是注解方式的PonitCut.由此可见,当同一个Aspect中对同一个Join Point有两个Advice的话,执行的顺序与方法的名称有关.


薛定饿
点滴记录
96 声望
8 粉丝
0 条评论
推荐阅读
Spring Cloud(3) - 服务治理: Spring Cloud Eureka
Spring Cloud Eureka 是 Spring Cloud 中的一个组件,它是基于 Netflix Eureka 做了二次封装,主要是负责完成微服务框架中服务治理的功能。Spring Cloud通过为 Eureka 增加了 Spring Boot 风格的自动化配置,我们...

薛定饿阅读 1.8k

PHP转Go实践:xjson解析神器「开源工具集」
我和劲仔都是PHP转Go,身边越来越多做PHP的朋友也逐渐在用Go进行重构,重构过程中,会发现php的json解析操作(系列化与反序列化)是真的香,弱类型语言的各种隐式类型转换,很大程度的减低了程序的复杂度。

王中阳Go10阅读 1.7k评论 2

封面图
万字详解,吃透 MongoDB!
MongoDB 是一个基于 分布式文件存储 的开源 NoSQL 数据库系统,由 C++ 编写的。MongoDB 提供了 面向文档 的存储方式,操作起来比较简单和容易,支持“无模式”的数据建模,可以存储比较复杂的数据类型,是一款非常...

JavaGuide5阅读 725

封面图
与RabbitMQ有关的一些知识
工作中用过一段时间的Kafka,不过主要还是RabbitMQ用的多一些。今天主要来讲讲与RabbitMQ相关的一些知识。一些基本概念,以及实际使用场景及一些注意事项。

lpe2348阅读 1.9k

封面图
计算机网络连环炮40问
本文已经收录到Github仓库,该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点,欢迎star~

程序员大彬7阅读 1k

Git操作不规范,战友提刀来相见!
年终奖都没了,还要扣我绩效,门都没有,哈哈。这波骚Git操作我也是第一次用,担心闪了腰,所以不仅做了备份,也做了笔记,分享给大家。问题描述小A和我在同时开发一个功能模块,他在优化之前的代码逻辑,我在开...

王中阳Go5阅读 2.2k评论 2

封面图
Redis 发布订阅模式:原理拆解并实现一个消息队列
“65 哥,如果你交了个漂亮小姐姐做女朋友,你会通过什么方式将这个消息广而告之给你的微信好友?““那不得拍点女朋友的美照 + 亲密照弄一个九宫格图文消息在朋友圈发布大肆宣传,暴击单身狗。”像这种 65 哥通过朋...

码哥字节6阅读 1.4k

封面图
96 声望
8 粉丝
宣传栏