Spring循环依赖原理分析
大家都清楚Spring框架实现类Ioc,即实现了依赖的自动注入,这是Spring的基本功能之一,并且对于循环依赖,Spring也可以自动注入,但是前提是存在循环依赖关系的Bean必须是单例的,原型的不可以。
1.什么是循环依赖
简单来说就是两个类的相互引用,举个例子来说,A类定义一个B类的实例字段,B类也定义一个A类的实例字段。
class A{
@Autowired
private B b;
//省略get,set方法
}
class B{
@Autowired
private A a;
//省略get,set方法
}
当加载a或b实例时就会发生,那么Spring是怎么处理的呢?
2.Spring处理循环依赖思路
由于处理循环依赖贯穿在整个Spring加载bean,实例化bean,装配bean属性过程中,处理逻辑比较复杂。需要说一下,Spring加载bean的大致过程,总结的比较粗线条,有很多扩展的细节,包括FactoryBean处理,不详细介绍,想了解更多只能分析源码。
- 读取配置文件或注解元数据生成BeanDefinition
- 根据BeanDefinition实例化Bean
- 填充Bean的属性字段,如果存在属性是其他Bean,并且还未实例化,需要实例化该Bean,完成填充
- 初始化Bean
3.伪代码模拟实现
定义几个保存Bean的集合,以及一个方法。
Map<String, Object> singletonObjects; //保存已经完成初始化的单例的map,key是beanName
//下面的定义就是为了解决循环依赖
Map<String, ObjectFactory<?>> singletonFactories; //创建Bean的工厂map,ObjectFactory是个接口,只有一个方法getObject(),返回Bean
Map<String, Object> earlySingletonObjects;//保存还未装配属性的Bean
Set<String> singletonsCurrentlyInCreation;//保存正在创建过程中的Bean
Object getBean(String beanName){
Object singletonObject;
//1.在缓存中获取,这里缓存的是初始化完成的bean
if(!singletonObjects.get(beanName)){
singletonObject= singletonObjects.get(beanName)
}
//2.如果当前bean正在创建过程中,其实也就是存在循环依赖,不然singletonsCurrentlyInCreation不会包含beanName
if(singletonsCurrentlyInCreation.contatins(beanName)){
if(earlySingletonObjects.get(beanName)==null){ //去实例化但未装配属性的bean结合中获取
singletonObject = singletonFactory.getObject();//去对象创建工厂中获取
earlySingletonObjects.put(beanName, singletonObject);
singletonFactories.remove(beanName);
}
}
//缓存不存在
if(singletonObject==null){
//开始实例化
singletonsCurrentlyInCreation.add(beanName);
singletonObject= instantiation(beanName); //伪代码,没有具体实现,假设创建了Bean的实例
//将创建bean的工厂缓存,其实通过getObject方法返回的就是上面刚刚创建的singletonObject对象,
//Spring之所以这么处理,而不是直接缓存到earlySingletonObjects集合,因为在实际执行getObject方法是,
//可以进行扩展,用户可以定制getObject的行为。
singletonFactories.put(beanName, beanName-> return singletonObject);
earlySingletonObjects.remove(beanName);
//装配bean属性,这里对于还未实例化的Bean引用会递归调用getBean方法开始创建,这是理解循环依赖的关键
populateBean(singletonObject);//伪代码,没有具体实现,装配Bean的属性
//bean属性装配完毕,将正在创建的beanName移除
singletonsCurrentlyInCreation.remove(beanName);
singletonObjects.put(beanName, singletonObject);//加入到singletonObjects
singletonFactories.remove(beanName);
earlySingletonObjects.remove(beanName);
initial(singletonObject);//初始化bean
}
return singletonObject;
}
上面方法大致模拟了Spring加载bean的流程,实际的过程要比这复杂的多,灵活的得多,这只是为说明循环依赖的解决思路,提取出的部分逻辑。
4.过程分析
以A依赖B,B依赖A为例,按照上面的伪代码,分析A加载过程,从getBean(a)开始分析
-
singletonObjects集合中不存在A的实例a,singletonsCurrentlyInCreation中也不存“a”,所以会执行A的实例化逻辑,先将“a”加入到singletonsCurrentlyInCreation集合,表示“a”正在创建过程中。
-
创建BeanName为a的实例
-
将创建a实例的工厂加入到singletonFactories中,简单来说,调用getObject方法时,就是返回singletonObject
-
装配a的属性
-
发现引用B的实例,开始调用方法getBean(b),与上面的getBean(a)相似
-
...
-
...
-
装配b的属性
-
发现引用A的实例,开始调用方法getBean(a)
-
singletonObjects不存在a的实例
-
a正在创建过程中,singletonsCurrentlyInCreation包含“a”
-
earlySingletonObjects不存在a的实例
-
去工厂中获取a的实例,其实就是第一次创建a的实例的带下划线的singletonObject
-
singletonObject放在earlySingletonObjects缓存
-
singletonObject不是null,后面的逻辑不执行
-
-
b装配完毕,singletonsCurrentlyInCreation中移除“b”
-
将b缓存到singletonObjects,singletonFactories移除b的工厂,earlySingletonObjects移除b
-
初始化b
-
-
a装配完毕,singletonsCurrentlyInCreation中移除“a”
-
将a缓存到singletonObjects,singletonFactories移除a的工厂,earlySingletonObjects移除a
-
初始化a
至此a加载完毕,b也加载完毕,也解决了循环依赖问题。
以上只是本人学习Spring的Bean加载过程的学习笔记,理解的不准确的地方欢迎大佬拍砖,如果有帮到您欢迎点赞收藏。