👺 循环依赖
2022年6月20日
- frame
👺 循环依赖
1. 前提
- 出现循环依赖的 Bean 必须要是 单例 (
singleton
)prototype
下,Spring 直接抛出异常- 假设 AB 均为
prototype
&& AB 循环依赖 - 当 实例化 A1 时,发现依赖 B,然后 实例化 B1 时,发现依赖 A,然后 实例化 A2,发现依赖 B,然后 实例化 B2,...
- 无限循环,该问题无法解决,还会导致系统崩溃
- 假设 AB 均为
- 依赖注入的方式不能全是构造器注入的方式
- bean 的生命周期中的
实例化
&属性赋值
是 两个阶段,只有在属性赋值
这一阶段才可能有操作空间, - 只有
实例化
完成后才可能将 半成品对象 放入三级缓存
- bean 的生命周期中的
2. 可以解决的情况
这里假设 A 先进行创建工作
A 注入 B 方式 | B 注入 A 方式 | 是否可以解决 |
---|---|---|
属性注入 | 属性注入 | 😋 😋 |
构造器注入 | 构造器注入 | ☹️ |
setter() | 构造器注入 | 😋 😋 |
构造器注入 | setter() | ☹️ |
3. 三级缓存
Map<String,Object> singletonObjects
- 一级缓存
- 保存已经 实例化、属性赋值、初始化 完成的 bean 单例
Map<String,Object> earlySingletonObjects
- 二级缓存
- 保存完成 实例化 的 bean 单例,这些 bean 也称为 早期曝光 对象
Map<String,ObjectFactory<?>> singletonFactories
- 三级缓存
- 保存 bean 的创建工厂,便于以后发生循环依赖时可以创建代理对象 or 原本对象
是否可用二级缓存?
- 二级缓存,只能是
一级缓存 + 二级缓存
- 若为
一级缓存 + 三级缓存
,在获取 singleton 时,发现一级缓存不存在,直接通过 工厂创建 - 此时无法确保工厂之前是否已经生成该 半成品 类
- 若生成 半成品类 后,从 三级缓存中删除,那么除了初次工厂制造,后续无法工厂创建,但我们也无法得到 工厂创建 的半成品类,因为不知道保存在哪里
- 若生成 半成品类 后,不从 三级缓存中删除,那么并发下,发现一级缓存不存在,工厂可能创建多个 半成品类,破坏单例模式,同时,生成的 半成品类 也没有保存在特定结构中
- 若为
一级缓存 + 二级缓存
,在创建 singleton 时,直接放入 二级缓存
- 若为
- 若没有代理对象生成的情况下:
- 可以解决循环依赖问题
- 在获取 singleton 时,发现一级缓存不存在,去二级缓存查找,找到即可直接返回
- 若有代理对象生成的情况下:
- 无法解决循环依赖问题
- 在创建 singleton 时,直接放入 二级缓存,此时放入的是 半成品普通对象,而非该类的 代理对象
- 在获取 singleton 时,发现一级缓存不存在,去二级缓存查找,找到的是 半成品普通对象
- 获取到之后,进行后续的 初始化 过程,在
BeanPostProcessor
过程中会转变成 代理对象,覆盖掉原有 半成品普通对象 - 从而可能导致多线程下获取到的 bean 对象不一致,有的线程获取的是 普通对象,有的获取的是 代理对象
4. 源码相关
1) singleton 创建时
/**
* 主要包含以下步骤:
* 1. 实例化,创建 bean 实例
* 1.1 实例化完成后,如果满足 提前暴露 条件,需要将该 bean 放入 三级缓存
* 2. 填充属性
* 3. bean 初始化
*
* @param beanName the name of the bean
* @param mbd the merged bean definition for the bean
* @param args explicit arguments to use for constructor or factory method invocation
* @return a new instance of the bean
* @throws BeanCreationException if the bean could not be created
* @see #instantiateBean
* @see #instantiateUsingFactoryMethod
* @see #autowireConstructor
*/
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
// 实例化、...
// 从这里可以看出,要放入三级缓存前,生产的 bean 必须已经完成实例化过程
// ------ 解决循环依赖问题 -----------
// 判断是否可以提前暴露
boolean earlySingletonExposure = (mbd.isSingleton() // 单例
&& this.allowCircularReferences // 允许循环依赖
&& isSingletonCurrentlyInCreation(beanName)); // 正被创建
// 满足提前暴露条件
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 将该 bean 放入 三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// 属性赋值、初始化...
}
2) singleton 获取时
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
// ......
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256); // 一级缓存
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16); // 二级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16); // 三级缓存
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 1. 尝试从一级缓存中获取
Object singletonObject = this.singletonObjects.get(beanName);
// 一级缓存中不存在,且 为单例 bean 的创建过程中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 2. 尝试从二级缓存中获取
singletonObject = this.earlySingletonObjects.get(beanName);
// 二级缓存不存在,且 允许提前暴露
if (singletonObject == null && allowEarlyReference) {
// 锁住一级缓存
synchronized (this.singletonObjects) {
// 再次确认,有点类似 单例模式双重检验加锁
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) { // 一级缓存不存在
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) { // 二级缓存也不存在
// 3. 尝试获取三级缓存的 bean 工厂
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
// 三级缓存中存在该 bean 的创建工厂,因为之前 createBean() 中发现可能发生循环依赖就会创建三级缓存
if (singletonFactory != null) {
// 通过该 bean 的创建工厂 创建该 单例 bean
// 这里创建的 bean 有两种:
// 若 bean 被 AOP 代理,则创建的是 bean 代理后的对象
// 若 baen 没有被 AOP 代理,则创建的是 bean 实例化后的对象
singletonObject = singletonFactory.getObject();
// 将创建的 bean 放入二级缓存,表示已经实例化,但还没有初始化
this.earlySingletonObjects.put(beanName, singletonObject);
// 该创建工厂已经完成创建工作,创建一次就不需要了,将其从 三级缓存中删除
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject; // 只要一级缓存 | 二级缓存中任何存在该单例 bean,直接返回
}
}