SpringAOP如何给通知advice排序

https://blog.csdn.net/qq_22167989/article/details/83962954

转自:Spring源码-AOP(九)-Advice排序 - 青离的个人空间 - OSCHINA - 中文开源技术交流社区

在使用Spring AOP时,有时会有在同一切入点存在多个Advice的情况出现,这里的多个Advice可能是不同的Advice,比如后置增强和环绕增强,也可能是多个相同的Advice,比如多个后置增强,甚至是更复杂的情况。因而就存在一个需求:不同Advice的执行顺序是怎样的,同样类型的Advice如何排序,一步一步来看。

1.Advice的执行顺序

对于不同类型的Advice执行顺序,大家都比较熟悉,但是为了比较更复杂的情况,还是先简单介绍下。

不同类型的Advice

Spring AOP中定义的基本增强有五种,分别是前置增强(BeforeAdvice),后置增强(AfterReturningAdvice),后置finally增强(AfterAdvice),抛出增强(AfterThrowingAdvice)和环绕增强(AroundAdvice)。

由于环绕增强可以通过ProceedingJoinPoint调用proceed方法执行原始方法,因此环绕增强可以分为环绕增强前(around before)和环绕增强后(around after),对应proceed方法的前后。

后置增强为return操作后操作,抛出增强为异常抛出时操作,故这两者是互斥的。后置finally增强,是finally类型的操作,因而不论方法是否有异常,都会执行,且在后置增强和抛出增强前执行。

用代码定义下以上Advice

 
  1. // 环绕增强

  2. @Around("pointcut()")

  3. public Object around(ProceedingJoinPoint pjp) throws Throwable{

  4. System.out.println("around before");

  5. Object retVal = pjp.proceed();

  6. System.out.println("around after");

  7. return retVal;

  8. }

  9. // 前置增强

  10. @Before("pointcut()")

  11. public void before(){

  12. System.out.println("before");

  13. }

  14. // 后置finally增强

  15. @After("pointcut()")

  16. public void after(){

  17. System.out.println("after");

  18. }

  19. // 后置return增强

  20. @AfterReturning("pointcut()")

  21. public void afterReturning(){

  22. System.out.println("after returning");

  23. }

  24. // 抛出增强

  25. @AfterThrowing(value="pointcut()",throwing="ex")

  26. public void afterThrowing(Exception ex){

  27. System.out.println("afterThrowing");

  28. }

通过测试执行的结果为

 
  1. around before

  2. before

  3. //method execute

  4. around after

  5. after

  6. after returning

如果方法抛出异常,结果为

 
  1. around before

  2. before

  3. //method execute

  4. after

  5. afterThrowing

因而对于五种增强类型的执行顺序如下

  1. 环绕增强前(around advice before)
  2. 前置增强(before advice)
  3. -- 方法执行
  4. 环绕增强后(around advice after)
  5. 后置finally增强(after advice)
  6. 后置增强/抛出增强

相同类型Advice的排序

如果存在两个相同类型的Advice,如何进行排序?答案Spring AOP不支持Advice层级的排序,只支持Aspect层级的排序。不同的Aspect通过实现Ordered接口,或使用@Order注解设置优先级。对于getOrder返回的值或@Order中设置的值,值越小优先级越高

 
  1. @Aspect

  2. public class AspectJAspect implements Ordered{

  3. @Override

  4. public int getOrder() {

  5. return 1;

  6. }

  7. }

或者

 
  1. @Aspect

  2. @Order(1)

  3. public class AspectJAspect{

  4. }

而多个Aspect中的相同类型Advice执行顺序,也分为两种情况:

  1. 方法执行之前的增强Advice,优先级高的先执行,如前置增强
  2. 方法执行之后的增强Advice,优先级高的后执行,如后置增强

且对于多个不同排序的Aspect中的多个Advice,一个Aspect执行完才到下一个Aspect,同样也是以原始方法的执行为界。

还以上面的多个Advice代码为例,如果存在两个Aspect,分别标注@Order(1)和@Order(2),@Order(1)对应的Advice打印内容后加1,@Order(2)对应的加2。以前置增强举例:

 
  1. @Aspect

  2. @Order(1)

  3. public class AspectJAspect1{

  4. @Before("pointcut()")

  5. public void before(){

  6. System.out.println("before 1");

  7. }

  8. }

  9. @Aspect

  10. @Order(2)

  11. public class AspectJAspect2{

  12. @Before("pointcut()")

  13. public void before(){

  14. System.out.println("before 2");

  15. }

  16. }

经过测试,打印结果如下:

 
  1. around before 1

  2. before 1

  3. around before 2

  4. before 2

  5. // method execute

  6. around after 2

  7. after 2

  8. after returning 2

  9. around after 1

  10. after 1

  11. after returning 1

对于这个测试结果,以后有类似的实现时还是要多注意的。至于原理,一是创建代理时形成的拦截器的链式调用,可见ReflectiveMethodInvocation的proceed方法,二是创建同一Aspect内的Advice有一个默认的排序方式,见ReflectiveAspectJAdvisorFactory中static代码库的METHOD_COMPARATOR创建。

2.@Order原理解析

Aspect类的排序,通过Ordered接口或@Order注解来实现。而注解情况下,获取完全部的Advice并封装成Advisor后,使用模板方法sortAdvisors完成了排序。

AbstractAdvisorAutoProxyCreator中的默认实现如下

 
  1. protected List<Advisor> sortAdvisors(List<Advisor> advisors) {

  2. OrderComparator.sort(advisors);

  3. return advisors;

  4. }

OrderComparator是Comparator的子类,通过compare方法比较两个对象

 
  1. public int compare(Object o1, Object o2) {

  2. return doCompare(o1, o2, null);

  3. }

  4. private int doCompare(Object o1, Object o2, OrderSourceProvider sourceProvider) {

  5. boolean p1 = (o1 instanceof PriorityOrdered);

  6. boolean p2 = (o2 instanceof PriorityOrdered);

  7. if (p1 && !p2) {

  8. return -1;

  9. }

  10. else if (p2 && !p1) {

  11. return 1;

  12. }

  13. // Direct evaluation instead of Integer.compareTo to avoid unnecessary object creation.

  14. int i1 = getOrder(o1, sourceProvider);

  15. int i2 = getOrder(o2, sourceProvider);

  16. return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;

  17. }

如果有Advisor实现了PriorityOrdered,而另一个没有实现,则实现PriorityOrdered的优先级更高。如果两个都没有实现PriorityOrdered,则继续比较getOrder方法返回的值。

 
  1. private int getOrder(Object obj, OrderSourceProvider sourceProvider) {

  2. Integer order = null;

  3. if (sourceProvider != null) {

  4. order = findOrder(sourceProvider.getOrderSource(obj));

  5. }

  6. return (order != null ? order : getOrder(obj));

  7. }

  8. protected int getOrder(Object obj) {

  9. Integer order = findOrder(obj);

  10. return (order != null ? order : Ordered.LOWEST_PRECEDENCE);

  11. }

  12. protected Integer findOrder(Object obj) {

  13. return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : null);

  14. }

如果Advisor实现了Ordered接口,则调用getOrder()方法,如果没实现,返回Ordered.LOWEST_PRECEDENCE,即Integer.MAX_VALUE。

到此排序的思路基本清晰了,但是突然发现一个问题,使用@Order的Aspect并没有实现Ordered接口啊,这里不是挂了吗?

这时回头看下sortAdvisors方法,进行排序的都是Advisor,并不是Aspect。而AspectJ方式的AOP,都是通过硬编码的方式指定了实际的Advisor类。XML方式的是AspectJPointcutAdvisor,注解方式的是InstantiationModelAwarePointcutAdvisorImpl,而这两个类都实现了Ordered接口。

注解方式的InstantiationModelAwarePointcutAdvisorImpl,其getOrder方法如下

 
  1. public int getOrder() {

  2. return this.aspectInstanceFactory.getOrder();

  3. }

这里的aspectInstanceFactory的类型为MetadataAwareAspectInstanceFactory,其实际的实现类为BeanFactoryAspectInstanceFactory,其为Aspect实例的工厂类

 
  1. public int getOrder() {

  2. Class<?> type = this.beanFactory.getType(this.name);

  3. if (type != null) {

  4. if (Ordered.class.isAssignableFrom(type) && this.beanFactory.isSingleton(this.name)) {

  5. return ((Ordered) this.beanFactory.getBean(this.name)).getOrder();

  6. }

  7. // Aspect非Ordered实现时处理

  8. return OrderUtils.getOrder(type, Ordered.LOWEST_PRECEDENCE);

  9. }

  10. return Ordered.LOWEST_PRECEDENCE;

  11. }

这里对Aspect类未实现Ordered接口进行了处理,查询Aspect是否有@Order注解,如果存在则返回其value值。另外如果不存在@Order,再去查询是否有javax.annotation.Priority注解,这是java通用注解包里支持优先级的注解。

 
  1. public static Integer getOrder(Class<?> type, Integer defaultOrder) {

  2. Order order = AnnotationUtils.findAnnotation(type, Order.class);

  3. if (order != null) {

  4. return order.value();

  5. }

  6. Integer priorityOrder = getPriority(type);

  7. if (priorityOrder != null) {

  8. return priorityOrder;

  9. }

  10. return defaultOrder;

  11. }

3.AspectJ方式的Advisor排序

在AOP AspectJ方式的驱动类AspectJAwareAdvisorAutoProxyCreator中,重写了sortAdvisors方法,使用AspectJPrecedenceComparator比较器代替之前的OrderComparator,但其内部实现还是使用OrderComparator,只是在优先级一样且是同一个Aspect时,对Advice的declarationOrder进行排序。declarationOrder目前并不能进行配置,而只是通过ReflectiveAspectJAdvisorFactory中的METHOD_COMPARATOR按规则对Advice赋值。

 
  1. private static final Comparator<Method> METHOD_COMPARATOR;

  2. static {

  3. CompoundComparator<Method> comparator = new CompoundComparator<Method>();

  4. comparator.addComparator(new ConvertingComparator<Method, Annotation>(

  5. new InstanceComparator<Annotation>(

  6. Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),

  7. new Converter<Method, Annotation>() {

  8. @Override

  9. public Annotation convert(Method method) {

  10. AspectJAnnotation<?> annotation =

  11. AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);

  12. return (annotation != null ? annotation.getAnnotation() : null);

  13. }

  14. }));

  15. comparator.addComparator(new ConvertingComparator<Method, String>(

  16. new Converter<Method, String>() {

  17. @Override

  18. public String convert(Method method) {

  19. return method.getName();

  20. }

  21. }));

  22. METHOD_COMPARATOR = comparator;

  23. }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值