盒子
盒子
文章目录
  1. 1. 循环依赖发生的时机
  2. 2. 构造器循环依赖
  3. 3. 单例setter循环依赖
  4. 4. 非单例循环依赖

Spring如何解决循环依赖问题

循环依赖就是N个类循环嵌套引用。

1. 循环依赖发生的时机

Spring单例对象的初始化大略分为三步:

  1. createBeanInstance: 实例化,其实就是调用对象的构造方法进行实例化对象
  2. populateBean: 填充属性,这一步主要是多bean的依赖属性进行填充
  3. initialzeBean: 初始化
    循环依赖主要发生在第一、第二步。也就是构造器循环依赖和field循环依赖

2. 构造器循环依赖

抛出BeanCurrentlylnCreationException异常

3. 单例setter循环依赖

Spring为了解决单例的循环依赖问题,使用了三级缓存
先让最底层对象完成初始化,通过三级缓存与二级缓存提前暴露创建中的bean,让其他bean率先完成初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/** 一级缓存:完成初始化的单例对象的cache **/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

/** 二级缓存:完成实例化但尚未填充属性初始化的单例对象的cache */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

/** 三级缓存:进入实例化阶段单例对象工厂的cache */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

/**
bean 的获取过程:先从一级获取,失败再从二级、三级里面获取

创建中状态:是指对象已经 new 出来了但是所有的属性均为 null 等待被 init
*/

检测循环依赖的过程如下:

  • A创建过程中需要B,于是A将自己放入三级缓存里面,去实例化B
  • B实例化的时候发现需要A,于是B先检查一级缓存,没有在查二级缓存,还是没有,则在查三级缓存,找到了!
    • 然后把三级缓存里的A放入二级缓存,并删除三级缓存里的A
    • B顺利初始化完毕,将自己放入一级缓存(此时B里面的A依然是创建中状态)
  • 然后回来接着创建A,此时B已经创建结束,直接从一级缓存中拿到B,然后完成创建,并将自己放入一级缓存中
  • 如此依赖便解决了循环依赖的问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 以上叙述的源码
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}

4. 非单例循环依赖

Spring无法完成依赖注入。因为Spring不缓存“prototype”作用域的bean,因此无法提前暴露一个创建中的bean

支持一下
扫一扫,支持沈健
  • 微信扫一扫
  • 支付宝扫一扫