在Spring中如何处理循环依赖问题
作者:MuseLss
Spring解决循环依赖的关键在于提前曝光未完全创建的bean,在Spring中创建Bean分为实例化、属性填充和初始化三步,循环依赖的解决思路是先创建A的bean实例,此时A是不完整的,用一个Map保存不完整的A,再创建B,B需要注入A
Spring如何处理循环依赖
解决的关键就在于提前曝光未完全创建的bean。
在Spring中创建Bean分为三步
- 实例化,createBeanInstance,即new一个bean对象。
- 属性填充,populateBean,即往bean对象中set属性值。
- 初始化,initializeBean。
循环依赖的解决思路
- 先创建A的bean实例,此时的A是不完整的,因为没有属性填充(即B依赖没有注入),此时用一个Map保存不完整的A,
- 再创建B,B需要注入A,所以可以从Map中得到不完整的A,此时B就完整了,然后A就可以注入B了。
在Spring中,只有同时满足以下两点才能解决循环依赖的问题。
1.依赖的bean必须都是单例。
- 因为如果是原型模式的话是需要创建一个新的对象,创建A1的时候,需要创建A1的依赖B1
- 那么在创建B1的时候,又需要创建B1的依赖A2,这样就要创建B2,A3,B3……,进入无限的创建对象的过程
2.依赖注入的方式,不能全是构造函数注入。
- 如果全是构造函数注入,即A(B b) ,那么表明在创建A的Bean的实例的时候,就需要得到B,那么此时就要创建B的bean实例,但是B也是要在构造函数中注入A,即B(A a),此时B需要在Map中找到不完整的A,但是发现找不到,因为A的Bean实体还没创建完(还在等着B)。
- 注意:Spring容器是按照字母的顺序创建 Bean的,因此循环依赖中,字母排在前面的Bean不能采用构造函数注入。
Sping解决循环依赖全流程
首先了解Spring bean相关的三个Map
singletonObject
,存放所有创建完毕的单例bean(完整的bean,即已经完成实例化并进行属性填充)。earlySingletonObjects
,存放仅完成实例化,但未进行属性填充和初始化的Bean。singletonfactories
,存放能创建Bean的工厂,通过这个工厂能获得bean,延迟bean生成,工厂生成的bean会放到earlySingletonObjects中。
在实例化bean后,Spring是不知道当前bean有没有循环依赖的,它会义无反顾的往singletonfactories中存放当前bean的工厂,这个步骤就是提前曝光
然后开始属性注入,此时bean A发现要注入bean B,所以请执行getBean(B)
- 先去singletonObject里找有没有,如果有则进行返回
- 如果没有,则判断Bean是否在创建中,如果不在创建中,则返回null
- 如果在创建中,则去earlySingletonObjects找,如果有则进行返回
- 如果没有,则去singletonfactories找到这个bean的工厂,通过工厂去创建bean,并存放到earlySingletonObjects中
- 如果singletonfactories没有找到bean的工厂就返回null
- 如果返回null,说明bean还没有创建,这个时候会先把这个bean标记为创建中,再调用doCreateBean(即,实例化,属性填充,初始化三个步骤)
此时就到了B这个bean属性注入的步骤了,调用了getBean(A),A此时在singletonfactories中找到提前暴露的工厂的到了A,然后把A从singletonfactories中删除,放到earlySingletonObjects中。
此时B属性注入成功,然后进行初始化,最后B存放到singletonObject中。
此时又回到了A注入B的地方,完成了对B的注入,然后A也从earlySingletonObjects删除,存放到singletonObject中。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。