忙,是我这个月的主旋律,也是我频繁鸽文章的接口————蛮三刀把刀
公司这两个月启动了全新的项目,项目排期满满当当,不过该学习还是要学习。这不,给公司搭项目的时候,踩到了一个Spring AOP的坑。
本文内容重点:
问题描述
Spring AOP执行顺序
探究顺序错误的真相
代码验证
结论
本文阅读大概需要:3分钟
码字不易,求个关注,欢迎关注我的个人原创公众号:后端技术漫谈(二维码见文章底部)
问题描述公司新项目需要搭建一个新的前后分离HTTP服务,我选择了目前比较熟悉的SpringBoot Web来快速搭建一个可用的系统。
鲁迅说过,不要随便升级已经稳定使用的版本。我偏不信这个邪,仗着自己用了这么久Spring,怎么能不冲呢。不说了,直接引入了最新的SprinBoot 2.3.4.RELEASE版本,开始给项目搭架子。
起初,大多数的组件引入都一切顺利,本以为就要大功告成了,没想到在搭建日志切面时栽了跟头。
作为一个接口服务,为了方便查询接口调用情况和定位问题,一般都会将请求日志打印出来,而Spring的AOP作为切面支持,完美的切合了日志记录的需求。
之前的项目中,运行正确的切面日志记录效果如下图:
可以看到图内的一次方法调用,会输出请求url,出入参,以及请求IP等等,之前为了好看,还加入了分割线。
我把这个实现类放入新项目中,执行出来却是这样的:
我揉了揉眼睛,仔细看了看复制过来的老代码,精简版如下:
/** * 在切点之前织入 * @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执行顺序的的资料,大多数时候,你会查到如下的答案:
正常情况 异常情况 多个切面的情况所以@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由于过于庞大,官网的文档已经到了冗杂的地步,不过最终还是找到了: