开心一刻
那天知道她结婚了,我整整一个晚上没睡觉,开了三百公里的车来到她家楼下,缓缓的抽了一支烟......
天渐渐凉了,响起了鞭炮声,迎亲车队到了,那天披着婚纱的她很美,真的很美!
我跟着迎亲车队开了几公里的时候,收到了她的信息:别送了,别送了,你的手扶拖拉机太响了 ......
前情回顾
楼主一而再,再而三的折腾循环依赖,你们不烦,楼主自己都烦了,如果你们实在是受不了,那就...
言归正传,虽然确实有点像懒婆娘的裹脚布,又臭又长,但确实还是有点东西的,只要大家坚持看完,肯定会有收获的!
我们先回顾下前三探
一探
Spring 的循环依赖,源码详细分析 → 真的非要三级缓存吗 中讲到了循环依赖问题
Spring 通过三级缓存解决 setter
singletonObjects
earlySingletonObjects
singletonFactories
着重分析了是否一定需要三级缓存来解决循环依赖问题
二探
Spring
再探循环依赖 → Spring 是如何判定原型循环依赖和构造方法循环依赖的,从源码的角度分析了 Spring
Set<String> singletonsCurrentlyInCreation 会记录当前正在创建中的实例名称, Spring 创建实例对象之前,会判断 singletonsCurrentlyInCreation
ThreadLocal<Object> prototypesCurrentlyInCreation 会记录当前线程正在创建中的原型实例名称, Spring 创建原型实例对象之前,会判断 prototypesCurrentlyInCreation
三探
三探循环依赖 → 记一次线上偶现的循环依赖问题,从源码的角度分析了这次偶现问题可能出现的原因
BeanDefinition
BeanDefinition 覆盖, @Configuration + @Bean 修饰的 BeanDefinition 会覆盖 @Component 修饰的 BeanDefinition , BeanDefinition 的覆盖并不影响 BeanDefinition
Bean
一通分析下来,虽说没能找到问题的真正原因,但至少知道了如何去规避这个问题,如何正确的书写规范的代码
问题复现
Spring
BeanPostProcessor
SpringBoot 版本 2.0.3.RELEASE ,示例代码地址:spring-circular-beanpostprocessor
我们只需要关注三个类
ServiceAImpl 依赖 ServiceBImpl , ServiceBImpl 也依赖 ServiceAImpl
BeanPostProcessor
她俩的爱情信息:
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'serviceAImpl': Bean with name 'serviceAImpl' has been injected into other beans [serviceBImpl] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
此刻,楼主才明白,小丑竟是我自己!
问题分析
serviceAImpl 作为循环依赖的一部分注入到了 serviceBImpl 后,又被包装了,这就意味着 serviceBImpl 引用的不是最终版本的 serviceAImpl
BeanPostProcessor ,楼主不想过多介绍,大家可以查看:Spring拓展接口之BeanPostProcessor,我们来看看它的底层实现
Spring
ServiceAImpl 比 ServiceBImpl 先被扫描,所以 serviceAImpl
serviceAImpl 填充属性serviceBImpl 完成之后,我们来 debug
debug 结果可以看到, ServiceBImpl 的实例对象 ServiceBImpl@5171 中注入的 ServiceAImpl 对象是 ServiceAImpl@5017
initializeBean(beanName, exposedObject, mbd); 后, Spring 暴露出来的 ServiceAImpl 的最终对象是 $Proxy53@5212
ServiceBImpl@5171 中注入的 ServiceAImpl@5017 并不是最终版本的 ServiceAImpl
问题处理
面对这样的问题,我们可以怎么处理了
@Lazy
@Lazy
@Lazy
或者
@Lazy
SmartInstantiationAwareBeanPostProcessor
BeanPostProcessor ,改用 SmartInstantiationAwareBeanPostProcessor
getEarlyBeanReference ,而非 postProcessAfterInitialization
ServiceAImpl
ServiceBImpl 对象填充 serviceAImpl 属性时,就用第三级缓存中的 ServiceAImpl
剔除循环依赖
循环依赖本就不合理,项目中应尽量避免
至于如何剔除,无法一概而论,需要大家自己去琢磨了
总结
循环依赖
Spring 通过三级缓存解决了 setter
循环依赖本就不合理,尽量去规避
真实项目问题
循环依赖 遇上 BeanPostProcessor
Shareding-JDBC 做分库,而 Shareding-JDBC 又通过 BeanPostProcessor
就这样,她俩就相遇了