最近和同事聊起来了springAOP的话题,说了到多个切面的时候程序是怎么执行的,我们常用的spring事务本身也是一个切面,使用的AOP原理,本人从网上找了一些资料,然后根据这些资料进行一下总结。
资料地址:1.https://blog.csdn.net/stpice/article/details/102733782
2.https://www.jb51.net/article/179078.htm
另外,如果有些朋友不是很清楚aop原理,可以去找我前面写的博客,讲解的简单易懂
正文:
配置AOP执行顺序的三种方式:
通过实现org.springframework.core.Ordered接口
1 2 3 4 5 6 7 8 9 10 |
@Component @Aspect @Slf4j public class MessageQueueAopAspect1 implements Ordered{ @Override public int getOrder() { // TODO Auto-generated method stub return 2 ; } } |
通过注解
1 2 3 4 5 6 7 8 |
@Component @Aspect @Slf4j @Order ( 1 ) public class MessageQueueAopAspect1{ ... } |
通过配置文件配置
1 2 3 4 5 6 |
< aop:config expose-proxy = "true" > < aop:aspect ref = "aopBean" order = "0" > < aop:pointcut id = "testPointcut" expression = "@annotation(xxx.xxx.xxx.annotation.xxx)" /> < aop:around pointcut-ref = "testPointcut" method = "doAround" /> </ aop:aspect > </ aop:config > |
我们在同一个方法上加以下两个AOP,看看究竟。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
@Component @Aspect @Slf4j public class MessageQueueAopAspect1 implements Ordered{ @Resource (name= "actionMessageProducer" ) private IProducer<MessageQueueInfo> actionProducer; @Pointcut ( "@annotation(com.xxx.annotation.MessageQueueRequire1)" ) private void pointCutMethod() { } //声明前置通知 @Before ( "pointCutMethod()" ) public void doBefore(JoinPoint point) { log.info( "MessageQueueAopAspect1:doBefore" ); return ; } //声明后置通知 @AfterReturning (pointcut = "pointCutMethod()" , returning = "returnValue" ) public void doAfterReturning(JoinPoint point,Object returnValue) { log.info( "MessageQueueAopAspect1:doAfterReturning" ); } //声明例外通知 @AfterThrowing (pointcut = "pointCutMethod()" , throwing = "e" ) public void doAfterThrowing(Exception e) { log.info( "MessageQueueAopAspect1:doAfterThrowing" ); } //声明最终通知 @After ( "pointCutMethod()" ) public void doAfter() { log.info( "MessageQueueAopAspect1:doAfter" ); } //声明环绕通知 @Around ( "pointCutMethod()" ) public Object doAround(ProceedingJoinPoint pjp) throws Throwable { log.info( "MessageQueueAopAspect1:doAround-1" ); Object obj = pjp.proceed(); log.info( "MessageQueueAopAspect1:doAround-2" ); return obj; } @Override public int getOrder() { return 1001 ; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
@Component @Aspect @Slf4j public class MessageQueueAopAspect2 implements Ordered{ @Resource (name= "actionMessageProducer" ) private IProducer<MessageQueueInfo> actionProducer; @Pointcut ( "@annotation(com.xxx.annotation.MessageQueueRequire2)" ) private void pointCutMethod() { } //声明前置通知 @Before ( "pointCutMethod()" ) public void doBefore(JoinPoint point) { log.info( "MessageQueueAopAspect2:doBefore" ); return ; } //声明后置通知 @AfterReturning (pointcut = "pointCutMethod()" , returning = "returnValue" ) public void doAfterReturning(JoinPoint point,Object returnValue) { log.info( "MessageQueueAopAspect2:doAfterReturning" ); } //声明例外通知 @AfterThrowing (pointcut = "pointCutMethod()" , throwing = "e" ) public void doAfterThrowing(Exception e) { log.info( "MessageQueueAopAspect2:doAfterThrowing" ); } //声明最终通知 @After ( "pointCutMethod()" ) public void doAfter() { log.info( "MessageQueueAopAspect2:doAfter" ); } //声明环绕通知 @Around ( "pointCutMethod()" ) public Object doAround(ProceedingJoinPoint pjp) throws Throwable { log.info( "MessageQueueAopAspect2:doAround-1" ); Object obj = pjp.proceed(); log.info( "MessageQueueAopAspect2:doAround-2" ); return obj; } @Override public int getOrder() { return 1002 ; } } |
1 2 3 4 5 6 |
@Transactional (propagation=Propagation.REQUIRES_NEW) @MessageQueueRequire1 @MessageQueueRequire2 public PnrPaymentErrCode bidLoan(String id){ ... } |
看看执行结果:
![](https://img-blog.csdnimg.cn/img_convert/8edab9b725b3cff1568fc4ca88f8ce2d.png)
从上面的测试我们看到,确实是order越小越是最先执行,但更重要的是最先执行的最后结束。
这个不难理解,Spring AOP就是面向切面编程,什么是切面,画一个图来理解下:
![](https://img-blog.csdnimg.cn/img_convert/610f072349670220b6d692944eb31382.png)
由此得出:spring aop就是一个同心圆,要执行的方法为圆心,最外层的order最小。从最外层按照AOP1、AOP2的顺序依次执行doAround方法,doBefore方法。然后执行method方法,最后按照AOP2、AOP1的顺序依次执行doAfter、doAfterReturn方法。也就是说对多个AOP来说,先before的,一定后after。
如果我们要在同一个方法事务提交后执行自己的AOP,那么把事务的AOP order设置为2,自己的AOP order设置为1,然后在doAfterReturn里边处理自己的业务逻辑。
总结:
如果两个切面中覆写的getOrder
方法中返回的是0,就是还没有显式的指定不同的顺序,所以,根据跟踪源码,可以发现在order相同的情况下, 是根据切面类的名称字母序进行优先级控制的,字母序越靠前,优先级越高。字母序的比较,首先将类名转换为字符串,然后调用String
的compareTo()
方法,对两个类名进行比对,决定切面的排序的。如果切面类使用了@Order
注解或者是实现了Ordered
接口,那么可以在比对的时候自动调用getOrder()
的方法,然后比较返回的值大小,值越小,优先级越高
。
同一个切面类中的方法,如果有多个不同的切入方式,例如@Around,@Before,@After,@AfterReturning,@AfterThrowing
,那么会先扫描出各个方法上的注解,对不同的方法按照上边注解的顺序进行排序,然后按照字母序进行排序,所以最终呈现出来的,同一个切面类中的不同切面方法的执行顺序,就会呈现如上所示的状态。