Spring下AOP部分失效部分生效的BUG解决
有助于您了解这个BUG是如何出现的,如果不想了解可以往最底下找到解决方式.
源码中
org.springframework.context.support.AbstractApplicationContext#refresh
这个方法你可以用转到类的方式 ctrl+上档键+T 在源码中找到他
这就是Spring的主体的加载核心,里面的每个调用方法都包含了巨量的代码,(大量if,大量的解析,大量的递归)
如果我说错了请指正,这是我自己对源码的感悟,可能有些偏差,
正常的加载流程
我们的类是会在finishBeanFactoryInitialization方法中进行调用,aop是优先级最高的,因为不优先生成,正常的类就会无法织入这个aop!
源码位置:
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
缓存读取
Spring在内部维护了一个三级缓存机制,既三个Map,这是我很惊叹的一点,细讲就很复杂,总之这个是用来处理循环依赖.
本身的装配
在缓存中没有实例情况下会将会开始装配这个beanName对应的类,靠的是反射
引入的bean装配
在第二部完成后就本身进进入缓存机制了,这时候开始对本身的引入比如使用 @Autowired 进行装配,一样进入到第一步,这里就是疯狂递归,但有了第一步就没有循环依赖的破事了
aop进行织入
最后才开始对aop进行织入,织入的意思是,aop把本身bean包裹形成一个新的bean,多个aop就是多包几次
装配bean是整个Spring的血肉,存在于几乎所有的流程中,至此你应该对Spring的是如何工作的有个大致的了解.
本次的问题就出现在了,后置处理器的装配流程中出现了不应该出现的bean,以至于aop处理后的类都没有生成,造成了部分bean失去了aop的关怀.
一个普普通通的上线日期
在每周四我们都会准备上线一些大版本的更新,我将要上线的需求成功部署后,准备点一杯胜利的奶茶,结果投诉接踵而,o(╥﹏╥)o
一个普普通通的BUG
经过简单的排查,我们发现是一个aop失去了作用,我们以为只是个简单的bug,我就没去管他,叫小伙伴把aop代码复制出来,手动改进去就好了,因为 同样的事情已经发生很多次了 ,
大量的AOP失效
随着下一个投诉,我隐约感觉不对劲,我用于藏匿测试数据的aop居然大面积失效了,这可不是一两个的问题,我马上查日志,我的aop的打桩一条都没有出现.
本地环境也发生了同样的问题
本地环境将线上分支载入,发现可以复现,这样就方便多了.此时我开始了面向浏览器的编程,但能找到我这文章的人,应该都知道aop失效的文章有多么多的水,一个复制另外一个.
已经运行了几个月的AOP失效
在百度的回答中,多的是配置错误xxx,注释错误xxx,但我的aop经过几个月的线上环境,就绝对不会是这个问题,问了2个小时的百度我放弃了,开始老老实实的进行代码分析,通俗点就是一个一个测.
第一次解决(并没有)
当我注释掉某个小伙伴的分支代码后,发现申请发生了一幕,代码运行正常了,我开开心心的提交了分支,并去吐槽了我那我懵逼的小伙伴,
开始频发报错
之后发生了一个查询的小bug,在提交后发现,又开始了,我开始怀疑人生,为什么一个简单的查询会让aop集体失效???最后线上环境,还是回滚到可以用的状态,第二天领导就找我谈话,叫我尽快解决这个问题,以保证后续的功能上线,o(╥﹏╥)o
思路开启
在百度翻了几页后,我发现了一个文章,虽然不是我最后发现的问题,但他给了我解决问题的灵感,文章中提到是因为类没有加懒加载注解,所以比aop更快生成bean,这时候我开始想到,我直接去源码里面打桩看看aop这货干啥不干活.
开始整活
去git找了5.1.4的Spring源码,下载后进行打桩,然后编译出jar,让项目走这个jar,如果有小伙伴想知道怎么整活,可以留言,我单独出一期咋让源码听你话.
首先我们要看bean装配中的第四部中到底有没有指定的数据
所以我在装配的开头对部分类进行打桩
结果发现了大秘密!!!
有问题的baen:
没有问题的bean:
至此我明白了一件事,这bug可能是一个提升自己的好机会,前所未见,骇人听闻,诡异至极.
错误的:
正确的:
然后我发现其中少掉的部分包含两个aop的bean
既然如此我开始以为是aop出了什么问题,开始打桩AOP的生成
源码位置:
org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors
只要有进入这个if就是aop的人了,就要走aop类型的bean装配.
打桩结果:
发现也没有太大问题,突然我们发现aop打桩,居然在错误bean生成的打桩后面!!!
说明什么,说明这个顺序是错的!!!
我开始怀疑是顺序生成时候是错的,但我没有证据,这时候我还没有开始怀疑是后置处理器的问题emmmmm,以至于我一整天都在各种尝试改变加载顺序,包括改加载的list,强行改beanName让他优先生成.
在底层游山玩水了这么久,干脆到上层打桩试试,我就从我最早的主线流程中进行打桩,当我走到registerBeanPostProcessors方法中,我的错误bean就被生成了,我就觉得纳闷,难道是这里?
我就开始了对registerBeanPostProcessors进行疯狂的断点打桩测试,但我缺忽略了这个方法的作用, ** 这个方法就不应该生成我需要的bean! **
就这样我在错误的思路下又折腾了半天.o(╥﹏╥)o
** 当我偶然间发现这个方法意思是叫register BeanPostProcessors **,我感觉了一丝不对劲,我想到,不对啊,这玩意不应该加载我的类啊!
很快我把工程中带有BeanPostProcessors注解注释掉就发生了神奇的一幕,所有aop全部正常工作!!!
把带有PostProcessors的所有方法都注释掉即可.
在Spring加载正常的PostProcessors中,递归了我们正常的bean,这registerBeanPostProcessors一般都是处理如jdbc/权限这样基础的类的后置处理器的加载,但bug可能发生在之前的某次更新,就出现一个不该出现的类.
在bean生成方式的第三步,进行了递归生成,造成aop未装配出bean就被装配,以至于没有织入.
但我并未找这个注入在哪里,所以我对这个错误发生的原因还是停留在猜想过程中.
在bean组装流程中,PostProcessors注解的类优先于常规bean进行生成,这个PostProcessors主要是数据库/权限这种东西的加载,是一个和aop毫无关系注解,但PostProcessors通过bean的装配将部分正常bean提前加载,以至于aop并没有及时进行织入造成了aop的大规模失效…