2

前言

用过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 粉丝