【线上排查实战】AOP切面执行顺序你真的了解吗

忙,是我这个月的主旋律,也是我频繁鸽文章的接口————蛮三刀把刀

公司这两个月启动了全新的项目,项目排期满满当当,不过该学习还是要学习。这不,给公司搭项目的时候,踩到了一个Spring AOP的坑。

本文内容重点:

问题描述

Spring AOP执行顺序

探究顺序错误的真相

代码验证

结论

本文阅读大概需要:3分钟

码字不易,求个关注,欢迎关注我的个人原创公众号:后端技术漫谈(二维码见文章底部)

问题描述

公司新项目需要搭建一个新的前后分离HTTP服务,我选择了目前比较熟悉的SpringBoot Web来快速搭建一个可用的系统。

鲁迅说过,不要随便升级已经稳定使用的版本。我偏不信这个邪,仗着自己用了这么久Spring,怎么能不冲呢。不说了,直接引入了最新的SprinBoot 2.3.4.RELEASE版本,开始给项目搭架子。

起初,大多数的组件引入都一切顺利,本以为就要大功告成了,没想到在搭建日志切面时栽了跟头。

作为一个接口服务,为了方便查询接口调用情况和定位问题,一般都会将请求日志打印出来,而Spring的AOP作为切面支持,完美的切合了日志记录的需求。

之前的项目中,运行正确的切面日志记录效果如下图:

【线上排查实战】AOP切面执行顺序你真的了解吗

可以看到图内的一次方法调用,会输出请求url,出入参,以及请求IP等等,之前为了好看,还加入了分割线。

我把这个实现类放入新项目中,执行出来却是这样的:

【线上排查实战】AOP切面执行顺序你真的了解吗

我揉了揉眼睛,仔细看了看复制过来的老代码,精简版如下:

/** * 在切点之前织入 * @param joinPoint * @throws Throwable */ @Before("webLog()") public void doBefore(JoinPoint joinPoint) throws Throwable { // 开始打印请求日志 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // 初始化traceId initTraceId(request); // 打印请求相关参数 LOGGER.info("========================================== Start =========================================="); // 打印请求 url LOGGER.info("URL : {}", request.getRequestURL().toString()); // 打印 Http method LOGGER.info("HTTP Method : {}", request.getMethod()); // 打印调用 controller 的全路径以及执行方法 LOGGER.info("Class Method : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName()); // 打印请求的 IP LOGGER.info("IP : {}", IPAddressUtil.getIpAdrress(request)); // 打印请求入参 LOGGER.info("Request Args : {}", joinPoint.getArgs()); } /** * 在切点之后织入 * @throws Throwable */ @After("webLog()") public void doAfter() throws Throwable { LOGGER.info("=========================================== End ==========================================="); } /** * 环绕 * @param proceedingJoinPoint * @return * @throws Throwable */ @Around("webLog()") public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { long startTime = System.currentTimeMillis(); Object result = proceedingJoinPoint.proceed(); // 打印出参 LOGGER.info("Response Args : {}", result); // 执行耗时 LOGGER.info("Time-Consuming : {} ms", System.currentTimeMillis() - startTime); return result; }

代码感觉完全没有问题,难道新版本的SpringBoot出Bug了。

显然,成熟的框架不会在这种大方向上犯错误,那会不会是新版本的SpringBoot把@After和@Around的顺序反过来了?

其实事情也没有那么简单。

Spring AOP执行顺序

我们先来回顾下Spring AOP执行顺序。

我们在网上查找关于SpringAop执行顺序的的资料,大多数时候,你会查到如下的答案:

正常情况

【线上排查实战】AOP切面执行顺序你真的了解吗

异常情况

【线上排查实战】AOP切面执行顺序你真的了解吗

多个切面的情况

【线上排查实战】AOP切面执行顺序你真的了解吗

所以@Around理应在@After之前,但是在SprinBoot 2.3.4.RELEASE版本中,@Around切切实实执行在了@After之后。

当我尝试切换回2.2.5.RELEASE版本后,执行顺序又回到了@Around-->@After

探究顺序错误的真相

既然知道了是SpringBoot版本升级导致的问题(或者说顺序变化),那么就要来看看究竟是哪个库对AOP执行的顺序进行了变动,毕竟,SpringBoot只是“形”,真正的内核在Spring。

我们打开pom.xml文件,使用插件查看spring-aop的版本,发现SpringBoot 2.3.4.RELEASE 版本使用的AOP是spring-aop-5.2.9.RELEASE。

而2.2.5.RELEASE对应的是spring-aop-5.2.4.RELEASE

于是我去官网搜索文档,不得不说Spring由于过于庞大,官网的文档已经到了冗杂的地步,不过最终还是找到了:

【线上排查实战】AOP切面执行顺序你真的了解吗

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpggpp.html