【日期】:2022/01/07
【问题】: 早上更新了下代码预备调试个接口却发现项目发动报了一个古怪的错误,如下图

【寻觅原因】: 从报错来看,大致意思是bean【a】和bean【c】存在循环依靠,bean【a】终究被包装,其他依靠的c并不是最后c的最后版别。 看了下项目里,类A,B,C的依靠关系为A -> B -> C -> A,的确存在间接循环依靠,但三个类里 都运用的特点注入方式,也就是set注入,在spring三级缓存的加持下应该会主动处理才对。 所以按照提示点击报错方位进入AbstractAutowireCapableBeanFactory中,报错方位如下

往上找,到createBean办法里,打上条件断点

以debug形式发动项目,进入断点




开端进行bean 【b】的创立






由于bean【c】的创立现已完结,所以此处回来为true,并把c加入actualDependentBeans中,最后actualDependentBeans集合肯定是不为空的,所以报错了。走完整个流程我们发现,终究报错的原因是bean 【c】依靠的目标【a】并不是a的终究版别,debug进程中也知道bean【a】的目标版别是在initializeBean中被改变,所以再次debug进入initializeBean办法,initializeBean中主要履行相关后置处理器在bean初始化前后做一些事情,包含applyBeanPostProcessorsBeforeInitialization和applyBeanPostProcessorsAfterInitialization两块,在前面的applyBeanPostProcessorsBeforeInitialization中回来的bean没有变化

而在后边的applyBeanPostProcessorsAfterInitialization办法中,当履行到AsyncAnnotationBeanPostProcessor处理器时,生成了署理目标,而实践生成署理的代码是在其父类AbstractAdvisingBeanPostProcessor中, 此刻大约猜到是由于@Async异步注解引起,项目里正好敞开了@EnableAsync(@EnableAsync敞开时它会向容器内注入AsyncAnnotationBeanPostProcessor), 且a类中刚好有一个办法带有此注解。

首先会判别假如已创立过署理(被事务署理等),isFrozen为false且切入点适配则新增一个切面即可,此处的切面AsyncAnnotationAdvisor完结,由于a类中存在标示了@Async的办法,所以是匹配的。当时a类还没创立过署理,走到后续创立署理流程

【处理方案】:
⓵ 把带有@Async注解的bean剔除循环依靠
⓶ 把allowRawInjectionDespiteWrapping设置为true
⓷ 运用@Lazy或@ComponentScan(lazyInit = true)处理
【总结】:
此处运用的SpringBoot版别为2.2.5, 其他版别待验证
假如bean存在以下关系并且A中含有标示@Async注解的办法:
- A,B两bean直接循环依靠, 如A->B->A
- A,B,C间接循环依靠, 如A->B->C->A
由于项目发动记载文件的次序不固定,在某些情况下
- 先createBean(A), 此刻把A的前期引证放入三级缓存
- populateBean(A), 开端创立A的依靠B
- createBean(B), populateBean(B), 开端创立B的依靠C
- createBean(C), populateBean(C), 此刻C将拿到的依靠A将是A存在三级缓存中的前期引证
- 完结C后续的创立,回到B的进程继续B的创立, B创立完结后回到A的进程
- 履行initializeBean(A), 在履行到applyBeanPostProcessorsAfterInitialization()阶段,循环到后置处理器AsyncAnnotationBeanPostProcessor时,由其父类对A进行类增强并生成新的署理目标露出给spring容器
- 到最后检测阶段发现C依靠的A并不是终究版别,导致报错。