相关文章能够查看: http://spring.hhui.topjava
前面两篇分别介绍了AOP的基本使用姿式和一些高级特性,当时还遗留了一个问题没有说明,即不一样的advice,拦截同一个目标方法时,优先级是怎样的,本篇博文将进行详细分析git
<!-- more -->github
在不分析源码的前提下,也只能经过实际的case来看优先级问题了,咱们如今设计一下使用实例,经过输出结果来看对应的优先级spring
首先建立被拦截的bean: com.git.hui.boot.aop.order.InnerDemoBean
安全
@Component public class InnerDemoBean { public String print() { try { System.out.println("in innerDemoBean start!"); String rans = System.currentTimeMillis() + "|" + UUID.randomUUID(); System.out.println(rans); return rans; } finally { System.out.println("in innerDemoBean over!"); } } }
接下来写一个切面,里面定义咱们常见的各类advicemvc
对于aop的使用,有疑问的能够参考: 190301-SpringBoot基础篇AOP之基本使用姿式小结dom
@Component @Aspect public class OrderAspect { @Pointcut("execution(public * com.git.hui.boot.aop.order.*.*())") public void point() { } @Before(value = "point()") public void doBefore(JoinPoint joinPoint) { System.out.println("do before!"); } @After(value = "point()") public void doAfter(JoinPoint joinPoint) { System.out.println("do after!"); } @AfterReturning(value = "point()", returning = "ans") public void doAfterReturning(JoinPoint joinPoint, String ans) { System.out.println("do after return: " + ans); } @Around("point()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { try { System.out.println("do in around before"); return joinPoint.proceed(); } finally { System.out.println("do in around over!"); } } }
使用SpringBoot的项目进行测试aop,使用仍是比较简单的spring-boot
@SpringBootApplication public class Application { private InnerDemoBean innerDemoBean; public Application(InnerDemoBean innerDemoBean) { this.innerDemoBean = innerDemoBean; this.innerDemoBean(); } private void innerDemoBean() { System.out.println("result: " + innerDemoBean.print()); } public static void main(String[] args) { SpringApplication.run(Application.class); } }
看下上面执行的输出结果学习
do in around before do before! in innerDemoBean start! 1552219604035|e9a31f44-6a31-4485-806a-834361842ce1 in innerDemoBean over! do in around over! do after! do after return: 1552219604035|e9a31f44-6a31-4485-806a-834361842ce1 result: 1552219604035|e9a31f44-6a31-4485-806a-834361842ce1
从输出结果进行反推,咱们能够知道统一切面中,advice执行的前后顺序以下测试
正常来说,拦截一个方法时,统一类型的切面逻辑都会写在一块儿,那这个case有什么分析的必要呢?
在咱们实际的使用中,同一类型的advice拦截同一个方法的可能性仍是很高的,why? 由于多个advice有本身定义的拦截规则,它们之间并不相同,但可能存在交集,好比咱们在上面的切面中,再加一个拦截注解的before advice
依然是上面的InnerDemoBean
,方法上加一个自定义注解
@AnoDot public String print() { try { System.out.println("in innerDemoBean start!"); String rans = System.currentTimeMillis() + "|" + UUID.randomUUID(); System.out.println(rans); return rans; } finally { System.out.println("in innerDemoBean over!"); } }
而后加一个拦截注解的advice
@Before("@annotation(AnoDot)") public void doAnoBefore(JoinPoint joinPoint) { System.out.println("dp AnoBefore"); }
再次执行前面的case,而后看下输出结果以下
In NetAspect doAround before! do in around before dp AnoBefore do before! in innerDemoBean start! 1552221765322|d92b6d37-0025-43c0-adcc-c4aa7ba639e0 in innerDemoBean over! do in around over! do after! do after return: 1552221765322|d92b6d37-0025-43c0-adcc-c4aa7ba639e0 In NetAspect doAround over! ans: 1552221765322|d92b6d37-0025-43c0-adcc-c4aa7ba639e0 result: 1552221765322|d92b6d37-0025-43c0-adcc-c4aa7ba639e0
咱们主要看下两个before,发现 AnoBefore
在前面; 所以这里的一个猜想,顺序就是根据方法命名的顺序来的,好比咱们再加一个 doXBefore
,而后咱们预估输出结果应该是
do AnoBefore > doBefore > doXBefore
额外添加一个
@Before("@annotation(AnoDot)") public void doXBefore(JoinPoint joinPoint) { System.out.println("dp XBefore"); }
接着就是输出结果以下,和咱们预期一致
咱们知道有个Order注解能够来定义一些优先级,那么把这个注解放在advice方法上,有效么?实际尝试一下
@Order(1) @Before(value = "point()") public void doBefore(JoinPoint joinPoint) { System.out.println("do before!"); } @Order(2) @Before("@annotation(AnoDot)") public void doAnoBefore(JoinPoint joinPoint) { System.out.println("dp AnoBefore"); } @Order(3) @Before("@annotation(AnoDot)") public void doXBefore(JoinPoint joinPoint) { System.out.println("dp XBefore"); }
若是注解有效,咱们预期输出结果以下
do Before > do AnoBefore > do XBefore
而后再次执行,看下输出结果是否和咱们预期同样
同一个切面中,相同的类型的advice,优先级是根据方法命名来的,加@Order
注解是没有什么鸟用的,目前也没有搜索到能够调整优先级的方式
若是说上面这种case不太好理解为啥会出现的话,那么这个可能就容易理解多了;毕竟一个切面完成一件事情,出现相同的advice就比较常见了;
好比spring mvc中,咱们一般会实现的几个切面
如今就须要再加一个切面,依然以before advice做为case
@Aspect @Component public class AnotherOrderAspect { @Before("@annotation(AnoDot)") public void doBefore() { System.out.println("in AnotherOrderAspect before!"); } }
接下来看测试输出结果以下图
发现了一个有意思的事情了,AnotherOrderAspect
切面的输出,彻底在OrderAspect
切面中全部的advice以前,接着咱们再次尝试使用@Order
注解来试试,看下会怎样
@Order(0) @Component @Aspect public class OrderAspect { } @Aspect @Order(10) @Component public class AnotherOrderAspect { }
若是顺序有关,咱们预期的输出结果应该是
do AnoBefore > do Before > doXBefore > do AnotherOrderAspect before!
实际测试输出以下,和咱们预期一致
从上面的测试来看,不一样的切面,默认顺序其实是根据切面的命令来的;
Order
注解来解决不一样切面的优先级问题,依然是值越小,优先级越高其实前面的case已经能够说明这个问题了,如今稍稍丰富一下AnotherOrderAspect
,看下结果
@Aspect @Order(10) @Component public class AnotherOrderAspect { @Before("@annotation(AnoDot)") public void doBefore() { System.out.println("in AnotherOrderAspect before!"); } @After("@annotation(AnoDot)") public void doAfter(JoinPoint joinPoint) { System.out.println("do AnotherOrderAspect after!"); } @AfterReturning(value = "@annotation(AnoDot)", returning = "ans") public void doAfterReturning(JoinPoint joinPoint, String ans) { System.out.println("do AnotherOrderAspect after return: " + ans); } @Around("@annotation(AnoDot)") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { try { System.out.println("do AnotherOrderAspect in around before"); return joinPoint.proceed(); } finally { System.out.println("do AnotherOrderAspect in around over!"); } } }
看下执行后的输出结果
假设A切面优先级高于B切面,那么咱们执行前后顺序以下
本篇内容有点多,针对前面的测试以及结果分析,给出一个小结,方便直接获取最终的答案
around 方法执行前代码 > before > 方法执行 > around方法执行后代码 > after > afterReturning/@AfterThrowing
统一切面中,同类型的advice的优先级根据方法名决定,暂未找到能够控制优先级的使用方式
不一样切面优先级,推荐使用 @Order
注解来指定,数字越低,优先级越高
优先级高的切面中的advice执行顺序会呈现包围优先级低的advice的状况,更直观的前后顺序,推荐看第四节的顺序图,更加清晰明了
一灰灰的我的博客,记录全部学习和工做中的博文,欢迎你们前去逛逛
尽信书则不如,以上内容,纯属一家之言,因我的能力有限,不免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
一灰灰blog
知识星球