一. Spring IOC 循环依赖?
参考地址:
《Spring IOC 容器源码分析 - 循环依赖的解决办法》
《Spring IOC循环依赖解决方案分析》
《Spring5源码阅读--如何解决循环依赖?》
在 Spring 中获取一个 Bean,是通过获取 BeanDefinition 实现的:在定义 Bean 信息的 XML 文件中,BeanDefinitionReader 读取指定路径下的 XML 文件,获取 Bean 定义信息并封装成 BeanDefinition 对象,该实例对象包含依赖关系信息 dependsOn。通常依赖关系是在 XML 的 p:xxx-ref,或者通过类的 @Autowired 等手段实现的。
循环依赖,就是在 classA 和 classB 的属性中,都互相包含彼此。Spring 避免循环依赖出现的错误,使用了三层缓存:
- 单例缓存 singletonObjects:存放填充完毕的,实际的 BeanDefinition
- Bean 定义缓存 earlySingletonObjects:存放未填充的 BeanDeinition (属性值全为 null),用于解决循环依赖问题;
- 工厂缓存 singletonFactories:存放单例 Bean 的工厂对象,在循环依赖问题中用来辅助解决问题;
- singletonFactories 的 key 为 beanName,value 为该 bean 对应的 bean 工厂;这样一个 bean 就可以通过 beanName 从对应的 bean 工厂中找到对应的 bean。
分析 getSingleton()
方法:
public Object getSingleton(String beanName){
//参数true设置标识允许早期依赖
return getSingleton(beanName,true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//检查缓存中是否存在实例
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//如果为空,则锁定全局变量并进行处理。
synchronized (this.singletonObjects) {
//如果此bean正在加载,则不处理
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
//当某些方法需要提前初始化的时候则会调用addSingleFactory 方法将对应的ObjectFactory初始化策略存储在singletonFactories
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//调用预先设定的getObject方法
singletonObject = singletonFactory.getObject();
//记录在缓存中,earlysingletonObjects和singletonFactories互斥
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
依旧以 classA 和 classB 为例,假设两个实例对象存在循环依赖关系,且 classA 对象首先在 Spring 容器中初始化。
- 构建 classA 对象的未填充 BeanDefinition 对象,并置入 earlySingletonObjects,同时**将该 bean 从工厂缓存 singletonFactories 中除掉**,为解决循环依赖做准备;
- 尝试向 classA 对象中填充内容,且填充过程到需要填充 classB 对象;
- 首先分别尝试从完全实例化完毕的单例缓存 singletonObjects 和不完全实例化的 earlySingletonObjects 中获取 classB 对象,都获取失败;
- 尝试初始化 classB 对象的 BeanDefinition。在初始化过程中,classB 对象需要引用到 classA 对象实例,此时出现了循环依赖的情况;
- classB 对象尝试从 singletonObjects 中获取 classA,但获取失败(因为此时 classA 当前还在初始化过程中,所以没有放入 singletonObjects 中);然后从 earlySingletonObjects 中获取 classA 的引用。
- classB 获取到 classA 的引用后,可以继续完成实例化过程;
- classB 实例化完成后,实例对象返回给 classA,然后 classA 完成其实例化过程。
至此,循环依赖的 classA 和 classB 都完成了实例化。