一文让你彻底弄懂 AspectJ 切面注解中五种通知注解的执行顺序

小郝不负流年
小郝不负流年   + 关注
2021-05-29 15:45:30   阅读18   评论0

一、前言

本文不讲什么是AOP,什么是切面,不知道的自己百度,本文主要是探究 AspectJ 切面注解中五种通知注解的执行顺序,以便于我们在实际开发过程中游刃有余的实现我们的业务。

首先我们知道切面通知注解有以下五种:

  • @Before: 前置通知, 在方法执行之前执行
  • @After: 后置通知, 在方法执行之后执行
  • @AfterRunning: 返回通知, 在方法返回结果之后执行
  • @AfterThrowing: 异常通知, 在方法抛出异常之后
  • @Around: 环绕通知, 围绕着方法执行

看到上面的描述我们大致也知道了一个执行顺序,但是在正常请求和有异常时具体的执行顺序我详细大家没有经过实测还是不敢确定的。另外如果同一个方法被多个Aspect类拦截呢它的执行顺序是怎样的呢?我想大家心里可能有猜想,但这些都需要我们实实在在的写一个例子来验证它。

二、编写测试代码

Aspect切面类


  1. /** 
  2.  * 测试 AspectJ 切面注解中五种通知注解 
  3.  * 
  4.  * @author Hoscen 
  5.  * @since 2021/5/29 14:36 
  6.  */  
  7. @Aspect  
  8. @Component  
  9. @Slf4j  
  10. @Order(1)  
  11. public class TestAspect {  
  12.   
  13.   
  14.     @Pointcut("execution(* cn.hoscen.test.testAspect.controller.*.*(..))")  
  15.     public void pointcut() {  
  16.     }  
  17.   
  18.     @Before(value = "pointcut()")  
  19.     public void doBefore(JoinPoint jp) {  
  20.         log.info(Thread.currentThread().getName() + "  doBefore");  
  21.     }  
  22.   
  23.     @Around(value = "pointcut()")  
  24.     public Object doAround(ProceedingJoinPoint pjp) throws Throwable {  
  25.         log.info(Thread.currentThread().getName() + "  doAround 1");  
  26.         Object result = pjp.proceed();  
  27.         log.info(Thread.currentThread().getName() + "  doAround 2");  
  28.         return result;  
  29.     }  
  30.   
  31.     @AfterThrowing(value = "pointcut()", throwing = "exception")  
  32.     public void doAfterThrowing(JoinPoint jp, Exception exception) {  
  33.         log.info(Thread.currentThread().getName() + "  doAfterThrowing");  
  34.         log.error("doAfterThrowing",exception);  
  35.     }  
  36.   
  37.     @AfterReturning(value = "pointcut()", returning = "result")  
  38.     public void doAfterReturning(JoinPoint jp, Object result) {  
  39.         log.info(Thread.currentThread().getName() + "  doAfterReturning");  
  40.     }  
  41.   
  42.     @After(value = "pointcut()")  
  43.     public void doAfter(JoinPoint jp) {  
  44.         log.info(Thread.currentThread().getName() + "  doAfter");  
  45.     }  
  46.   
  47. }  

测试Controller


  1. /** 
  2.  * 测试 AspectJ 切面注解中五种通知注解 
  3.  * 
  4.  * @author Hoscen 
  5.  * @since 2021/5/29 14:44 
  6.  */  
  7. @RestController  
  8. @RequestMapping(value = "/api/test", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})  
  9. public class TestAspectController extends BaseController {  
  10.   
  11.     @RequestMapping(value = "/ok", method = RequestMethod.GET)  
  12.     public BaseReturn getOk() {  
  13.         return BaseReturn.markSuccess(null);  
  14.     }  
  15.   
  16.     @RequestMapping(value = "/error", method = RequestMethod.GET)  
  17.     public BaseReturn getError() {  
  18.         int a = 0;  
  19.         int b = 1;  
  20.         LOGGER.info(b/a);  
  21.         return BaseReturn.markSuccess(null);  
  22.     }  
  23.   
  24. }  

三、执行代码观察结果

1、正常请求


  1. [INFO ] 2021-05-29 15:19:14.651 cn.hoscen.core.common.aop.TestAspect.doAround(TestAspect.java:34)  
  2. http-apr-9100-exec-4  doAround 1  
  3. [INFO ] 2021-05-29 15:19:14.651 cn.hoscen.core.common.aop.TestAspect.doBefore(TestAspect.java:29)  
  4. http-apr-9100-exec-4  doBefore  
  5. [INFO ] 2021-05-29 15:19:14.653 cn.hoscen.core.common.aop.TestAspect.doAround(TestAspect.java:36)  
  6. http-apr-9100-exec-4  doAround 2  
  7. [INFO ] 2021-05-29 15:19:14.653 cn.hoscen.core.common.aop.TestAspect.doAfter(TestAspect.java:53)  
  8. http-apr-9100-exec-4  doAfter  
  9. [INFO ] 2021-05-29 15:19:14.653 cn.hoscen.core.common.aop.TestAspect.doAfterReturning(TestAspect.java:48)  
  10. http-apr-9100-exec-4  doAfterReturning  

2、异常请求


  1. [INFO ] 2021-05-29 15:19:42.701 cn.hoscen.core.common.aop.TestAspect.doAround(TestAspect.java:34)  
  2. http-apr-9100-exec-6  doAround 1  
  3. [INFO ] 2021-05-29 15:19:42.701 cn.hoscen.core.common.aop.TestAspect.doBefore(TestAspect.java:29)  
  4. http-apr-9100-exec-6  doBefore  
  5. [INFO ] 2021-05-29 15:19:42.707 cn.hoscen.core.common.aop.TestAspect.doAfter(TestAspect.java:53)  
  6. http-apr-9100-exec-6  doAfter  
  7. [INFO ] 2021-05-29 15:19:42.707 cn.hoscen.core.common.aop.TestAspect.doAfterThrowing(TestAspect.java:42)  
  8. http-apr-9100-exec-6  doAfterThrowing  

四、得出结论与注意事项

1、执行顺序在正常和异常时有所不同,具体请看第三点的日志或自己把代码拿去跑一跑看一看。

2、对于@Around这个advice,不管它有没有返回值,但是必须要方法内部,调用一下 pjp.proceed();否则,Controller 中的接口将没有机会被执行,从而也导致了 @Before这个advice不会被触发。

3、如果同一个方法被多个Aspect类拦截呢,比如两个aspect,不管正常还是异常情况,aspect1 和 aspect2 的执行顺序都是未定的。可以通过给aspect添加@Order注解(该注解全称为:org.springframework.core.annotation.Order) 来指定顺序,值越小的 aspect 越先执行。具体情况可以下载我的测试源码自己跑一下看看就清楚了(测试同一个方法被多个Aspect类拦截,把切面类拷贝一份改个名和order就可以了),比如正常情况是下面这样

对我有用,我要     转载  
文章分类: Java  
所属标签: AspectJ  
  • 0条评论
  • 只看作者
  • 按时间|按热度
  • 由于本人多次涉及需要打印这个证明,而每次都会忘记入口,在网上各种搜索各种摸索很是浪费时间。故本次将操作流程整理记录下来,以备忘。同时也分享给大家。1、打开湖北政务服务网,地址:http://zwfw.hubei.gov.cn/s/index.html2、切换区域到“武汉市”3、在“特色服务”模块找到“(个人)武汉市社会保险公共服务平台”4、进入“(个人)武汉市社会保险公共服务平台”,登录账号密码<imgsrc="https://cdnstatic.hoscen.cn/blog/article/184053017752895488/img/497065960be44747825acb86a17483c1.png"style=
  • 本文讲触发el-dialog前动态修改窗口title的方法。1、el-dialog添加title属性el-dialog :title="titleType+'菜单'" :visible.sync="dialogVisible" width="800px" >el-dialog>  2、初始化变量(titleType,名称自己定义)export&
  • 如何使用postman模拟http发送xml参数报文的POST请求?1、postman工具通过安装软件或使用谷歌插件都可以,这里不再赘述。2、配置postman,选择POST,填写URL;切换到Headers,添加Content-Type:text/xml 3、切换到body,选择raw,XML,下方填写你的请求报文4、点击Send发送请求,如图可以看到响应状态、时间、结果等信息5、讲到这里就结束了,是不是学会了?快去试试吧!
  • 一般我们在使用CDN时都设置有缓存时间,当源站资源发生变更后,如果缓存时间没到,那么用户访问的依旧是变更前的数据,虽说又拍云控制台提供了缓存刷新功能界面,但是每次都手动去刷新显示不太理想,当然又拍云也想到了这一点,提供给我们有API可以调用。本篇文章就是讲解如何接入又拍云缓存刷新API。网站免费接入又拍云CDN的方法,请查看我另外一篇文章,地址: https://www.hoscen.cn/blog/hao/articles/204022774975430656.html又拍云API文档:https://api.upyun.com/doc#/api/guide/overview看完文档,我们会知道又拍云提供有两个缓存刷新接口,一个支持通配符(但次数有限),一个是完整url刷新。同时注意调用接口时将 Token 放入 HTTP Header 中 。那么我们需要3个接口:1、获取token2、URL刷新3、缓存批量刷新详细请求参数和响应值请查看文档。话不多说,我们直接放上核心代码1、获取token2、URL刷新<img src="https://cdnstatic.hoscen.cn/blog/article/
  • 很多时候我们需要Linux服务器定时去运行一个脚本来触发一个操作,比如写缓存数据到硬盘、定时备份、定时重启服务、定期清除日志等。下面就简单讲解一下Linuxcrontab命令如何实现自动循环执行shell脚本。一、准备shell脚本比如我们准备一个hello.shvim/hcn/sh/hello.sh#!/bin/bash  DATETIME=$(date"+%Y%m%d%H%M%S") echo"hello, www.hoscen.cn,时间:${DATETIME}"  通过chmod命令赋予该脚本的执行权限chmod755hello.sh测试正确性二、开启crontab服务 linux应该都有crontab,没有的话可以安装一下:yuminstall vixie-cronyuminstall crontabsvixie-cron软
  • 本文主要讲canonical标签的使用、canonical标签的作用、canonical标签SEO,在实践中如何正确规范的使用canonical标签。Canonical标签实际上就是一个页面内的301转向,可以帮助我们解决内容一样url不一样的网址规范化问题。和301跳转不同的是,用户并不被转向,但是对于搜索引擎来说,页面链接的权重是会被集中到代码中指明的规范化url上的。 图片来源:一灯出海对于经验丰富的SEO人员来说,canonical标签的使用一定不陌生,但最近在实践中发现不少网站的页面虽然用了canonical标签,但是使用方法却不规范。所以在这里和大家一起探讨一下canonical标签的规范使用方法,让更多的SEO人员避免走弯路。如果一个页面有多个url:https://www.hoscen.cn/blog/hao/articles/211896161185824768.htmlhttps://www.hoscen.cn/blog/hao/articles/211896161185824768/view这些url的页面内容完全一样,而我们想优化的规范化url为<ahref="https://www.hoscen.cn/blog/ha
  • 生活中难免有觉得“好累啊”的时候,岁月在不同阶段将不同压力倾轧到不同人身上,那些时刻降临时,多半只能靠自己挺过去,“人应该有力量,揪着自己的头发把自己从泥地里拔起来。”  10种疲累,10个“解药”。给累了的你。《如果,你觉得很累很累……》No.1明明休了周末,周一上班还是觉得:好累啊……一剂解药:所谓的休息并不单纯只有躺下,而是做自己想做的事,不做自己不想做的事情。休息有“储存的休息”和“释放的休息”两种。“储存的休息”通过休息来储存体力和活力。“释放的休息”则通过做喜欢的事情来释放平日累积的郁闷和压力。“储存的休息”不足时身体会坏掉,“释放的休息”不足时精神会崩坏。开药者丨@bibibi_senseiNo.2总是活在别人的眼光里,总是被他人的评价所左右,好累啊……一剂解药:纵使被说坏话
  • 1. 两数之和

    阅读数539
    总结:1、解决方法通常我们最容易想到的是暴力枚举(双重for循环),时间复杂度O(N^2),空间复杂度O(1),其中N是数组中的元素数量 2、利用哈希表可以以空间换时间,时间复杂度O(N),空间复杂度O(N),其中N是数组中的元素数量 题目给定一个整数数组nums和一个目标值target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。示例:给定nums=[2,7,11,15],target=9因为nums[0]+nums[1]=2+7=9所以返回[0,1]答案第一版:暴力枚举,时间复杂度高public int[] twoSum(int[]nums, int target){   int len=nums.length;   
  • 数据结构绪论

    阅读数538
    1、数据结构起源早期计算机被理解为数值计算工具,但是我们还有一些非数值的计算,因此需要一些更科学有效的手段(比如表、树、图等数据结构)的帮助来处理问题。所以数据结构是一门研究非数值计算的程序设计问题中的操作对象以及它们之间的关系和操作等相关问题的学科。2、基本概念和术语2.1 数据数据:是描述客观事物的符号,是计算机中可以操作的对象,是能够被计算机识别,并能输入给计算机处理的符号集合。数据不仅包括整型、实型等数值类型,还包括字符、声音、图像、视频等非数值类型。我们所说的数据,其实就是符号,但是这个符号需要满足两点:能输入到计算中能被计算机程序处理2.2 数据元素数据元素,也被称为记录。是组成数据的、具有一定意义的基本单位。比如,在人类中,人就是数据元素。在禽类中,猪狗牛羊等动物就是禽类的数据元素。2.3 数据项数据项:一个数据元素可以由若干个数据项组成。比如 人这个数据元素,就可以有眼、耳、鼻等数据项,也可以有姓名、年龄等数据项。具体有哪些数据项是视你所做的系统来决定。数据项是数据不可分割的最小单位。但注意真正讨论问题时,数据元素才是数据结构中建立数据模型的着眼点。就像我们讨论一个电影,通常是讨论角色这个“数据元素”,而不是讨论角色的姓名、年龄这些“数据项”。2.4 数据对象数据对象:是性
  • xxx:郝实诚!你真的叫这个名字啊? 我:对啊  不止一次....-- 大家好,我叫郝实诚,真的很实诚。