抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

SpringIOC-循环依赖

前言

​ 这篇分析 doCreateBean() 第三个过程:循环依赖处理。其实循环依赖并不仅仅只是在 doCreateBean() 中处理,其实在整个加载 bean 的过程中都有涉及,所以下篇内容并不仅仅只局限于 doCreateBean(),而是从整个 Bean 的加载过程进行分析。

什么是循环依赖

​ 循环依赖其实就是循环引用,就是两个或者两个以上的 bean 互相引用对方,最终形成一个闭环,如 A 依赖 B,B 依赖 C,C 依赖 A,如下:

​ 循环依赖 其实就是一个死循环的过程,在初始化 A 的时候发现引用了 B,这时就会去初始化 B,然后又发现 B 引用 C,跑去初始化 C,初始化 C 的时候发现引用了 A,则又会去初始化 A,依次循环永不退出,除非有终结条件。

Spring 循环依赖的场景有两种:

  1. 构造器的循环依赖
  2. field 属性的循环依赖

对于构造器的循环依赖,Spring 是无法解决的,只能抛出 BeanCurrentlyInCreationException 异常表示循环依赖,所以下面我们分析的都是基于 field 属性的循环依赖。

​ 在博客中提到,Spring 只解决 scope 为 singleton 的循环依赖,对于scope 为 prototype 的 bean Spring 无法解决,直接抛出 BeanCurrentlyInCreationException 异常。为什么 Spring 不处理 prototype bean,其实如果理解 Spring 是如何解决 singleton bean 的循环依赖就明白了。这里先卖一个关子,我们先来关注 Spring 是如何解决 singleton bean 的循环依赖的。

流程图

解决循环依赖

我们先从加载 bean 最初始的方法 doGetBean() 开始。

doGetBean

doGetBean() 中,首先会根据 beanName 从单例 bean 缓存中获取,如果不为空则直接返回。

1
Object sharedInstance = getSingleton(beanName);

getSingleton

调用 getSingleton() 方法从单例缓存中获取,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@Override
@Nullable
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//1.从单例对象缓存中获取beanName对应的单例对象
//先从一级缓存拿
Object singletonObject = this.singletonObjects.get(beanName);
//2.如果单例对象缓存中没有,并且该beanName对应的单例bean正在创建中
//如果bean还正在创建,还没创建完成,其实就是堆内存有了,属性还没有DI依赖注入
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 3.加锁进行操作
synchronized (this.singletonObjects) {
// 4.从早期单例对象缓存中获取单例对象(之所称成为早期单例对象,是因为earlySingletonObjects里
//从二级缓存中拿
singletonObject = this.earlySingletonObjects.get(beanName);
// 5.如果在早期单例对象缓存中也没有,并且允许创建早期单例对象引用
//如果还拿不到,并且允许bean提前暴露
if (singletonObject == null && allowEarlyReference) {
// 6.从单例工厂缓存中获取beanName的单例工厂
//从三级缓存中获取
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 7.如果存在单例对象工厂,则通过工厂创建一个单例对象
//注意这是一个钩子方法
singletonObject = singletonFactory.getObject();
//升级到二级缓存
// 8.将通过单例对象工厂创建的单例对象,放到早期单例对象缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
//删除三级缓存
// 9.移除该beanName对应的单例对象工厂,因为该单例工厂已经创建了一个实例对象,并且放到earlySingletonObjects缓存了,
// 因此,后续获取beanName的单例对象,可以通过earlySingletonObjects缓存拿到,不需要在用到该单例工厂
this.singletonFactories.remove(beanName);
}
}
}
}
// 10.返回单例对象
return singletonObject;
}
三级缓存的定义

这个方法主要是从三个缓存中获取,分别是:singletonObjects、earlySingletonObjects、singletonFactories,三者定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Cache of singleton objects: bean name to bean instance.
* 单例对象的缓存
* 一级缓存
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/**
* Cache of singleton factories: bean name to ObjectFactory.
* 三级缓存
*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/**
* Cache of early singleton objects: bean name to bean instance.
* 二级缓存
*/
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
意义如下
  • singletonObjects:单例对象的cache
  • singletonFactories : 单例对象工厂的cache
  • earlySingletonObjects :提前暴光的单例对象的Cache

他们就是 Spring 解决 singleton bean 的关键因素所在,我称他们为三级缓存,第一级为 singletonObjects,第二级为 earlySingletonObjects,第三级为 singletonFactories。这里我们可以通过 getSingleton() 看到他们是如何配合的,这分析该方法之前,提下其中的 isSingletonCurrentlyInCreation()allowEarlyReference

  • isSingletonCurrentlyInCreation():判断当前 singleton bean 是否处于创建中。bean 处于创建中也就是说 bean 在初始化但是没有完成初始化,有一个这样的过程其实和 Spring 解决 bean 循环依赖的理念相辅相成,因为 Spring 解决 singleton bean 的核心就在于提前曝光 bean。
  • allowEarlyReference:从字面意思上面理解就是允许提前拿到引用。其实真正的意思是是否允许从 singletonFactories 缓存中通过 getObject() 拿到对象,为什么会有这样一个字段呢?原因就在于 singletonFactories 才是 Spring 解决 singleton bean 的诀窍所在,这个我们后续分析。
执行流程

getSingleton() 整个过程如下:首先从一级缓存 singletonObjects 获取,如果没有且当前指定的 beanName 正在创建,就再从二级缓存中 earlySingletonObjects 获取,如果还是没有获取到且运行 singletonFactories 通过 getObject()获取,则从三级缓存 singletonFactories 获取,如果获取到则,通过其 getObject() 获取对象,并将其加入到二级缓存 earlySingletonObjects 中 从三级缓存 singletonFactories 删除,如下:

1
2
3
4
5
6
7
8
9
10
// 7.如果存在单例对象工厂,则通过工厂创建一个单例对象
//注意这是一个钩子方法
singletonObject = singletonFactory.getObject();
//升级到二级缓存
// 8.将通过单例对象工厂创建的单例对象,放到早期单例对象缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
//删除三级缓存
// 9.移除该beanName对应的单例对象工厂,因为该单例工厂已经创建了一个实例对象,并且放到earlySingletonObjects缓存了,
// 因此,后续获取beanName的单例对象,可以通过earlySingletonObjects缓存拿到,不需要在用到该单例工厂
this.singletonFactories.remove(beanName);

这样就从三级缓存升级到二级缓存了。

上面是从缓存中获取,但是缓存中的数据从哪里添加进来的呢?

单例bean提前暴露

一直往下跟会发现在 doCreateBean() ( AbstractAutowireCapableBeanFactory ) 中,有这么一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
//是否 单例bean提前暴露
// 7.判断是否需要提早曝光实例:单例 && 允许循环依赖 && 当前bean正在创建中
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
//beanName是否正在创建
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 8.提前曝光beanName的ObjectFactory,用于解决循环引用
//这里着重理解,对理解循环依赖帮助非常大,重要程度 5 添加三级缓存
// 8.1 应用后置处理器SmartInstantiationAwareBeanPostProcessor,允许返回指定bean的早期引用,若没有则直接返回bean
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

如果 earlySingletonExposure == true 的话,则调用 addSingletonFactory() 将他们添加到缓存中,但是一个 bean 要具备如下条件才会添加至缓存中:

  • 单例
  • 运行提前暴露 bean
  • 当前 bean 正在创建中
addSingletonFactory

addSingletonFactory() 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
// 1.如果beanName不存在于singletonObjects缓存中
//一级缓存不存在beanName
if (!this.singletonObjects.containsKey(beanName)) {
// 2.将beanName和singletonFactory注册到singletonFactories缓存(beanName -> 该beanName的单例工厂)
//设置beanName和匿名类的映射关系加入到三级缓存
this.singletonFactories.put(beanName, singletonFactory);
// 3.移除earlySingletonObjects缓存中的beanName(beanName -> beanName的早期单例对象)
this.earlySingletonObjects.remove(beanName);
// 4.将beanName注册到registeredSingletons缓存(已经注册的单例集合)
this.registeredSingletons.add(beanName);
}
}
}

从这段代码我们可以看出 singletonFactories 这个三级缓存才是解决 Spring Bean 循环依赖的诀窍所在。同时这段代码发生在 createBeanInstance() 方法之后,也就是说这个 bean 其实已经被创建出来了,但是它还不是很完美(没有进行属性填充和初始化),但是对于其他依赖它的对象而言已经足够了(可以根据对象引用定位到堆中对象),能够被认出来了,所以 Spring 在这个时候选择将该对象提前曝光出来让大家认识认识。

addSingleton

介绍到这里我们发现三级缓存 singletonFactories 和 二级缓存 earlySingletonObjects 中的值都有出处了,那一级缓存在哪里设置的呢?在类 DefaultSingletonBeanRegistry 中可以发现这个 addSingleton() 方法,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 1.添加到单例对象缓存,添加进一级缓存
this.singletonObjects.put(beanName, singletonObject);
//2.将单例工厂缓存移除(已经不需要),删除三级缓存
this.singletonFactories.remove(beanName);
//3.将早期单例对象缓存移除(已经不需要),删除二级缓存
this.earlySingletonObjects.remove(beanName);
// 4.添加到已经注册的单例对象缓存
this.registeredSingletons.add(beanName);
}
}
getSingleton

添加至一级缓存,同时从二级、三级缓存中删除。这个方法在我们创建 bean 的链路中有哪个地方引用呢?其实在前面博客 已经提到过了,在 doGetBean() 处理不同 scope 时,如果是 singleton,则调用 getSingleton(),如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//着重看,大部分是单例的情况
// 9.针对不同的scope进行bean的创建
// Create bean instance.
if (mbd.isSingleton()) {
//获取单例的实例,单例的核心入口
// 9.1 scope为singleton的bean创建(新建了一个ObjectFactory,并且重写了getObject方法)
sharedInstance = getSingleton(beanName, () -> {
try {
// 9.1.1 创建Bean实例,调用实例化的核心方法
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
//该方法是FactoryBean接口的调用入口
// 9.1.2 返回beanName对应的实例对象
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}else ...
getSingleton

前面几篇博客已经分析了 createBean(),这里就不再阐述了,我们关注方法 getSingleton() 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
// 1.加锁,避免重复创建单例对象
synchronized (this.singletonObjects) {
// 2.首先检查beanName对应的bean实例是否在缓存中存在,如果已经存在,则直接返回
Object singletonObject = this.singletonObjects.get(beanName);
//第一次调用必定为空
// 3.beanName对应的bean实例不存在于缓存中,则进行Bean的创建
if (singletonObject == null) {
// 4.当bean工厂的单例处于destruction状态时,不允许进行单例bean创建,抛出异常
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
// 5.创建单例前的操作
//把beanName添加到singletonsCurrentlyInCreation Set容器中,在这个集合里面的bean都是正在实例化的bean
beforeSingletonCreation(beanName);
boolean newSingleton = false;
// suppressedExceptions用于记录异常相关信息
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 6.执行singletonFactory的getObject方法获取bean实例
//如果这里有返回值,就代表这个bean已经结束创建了,已经完全创建成功
//调到lamda表达式的createBean(beanName, mbd, args);的方法
singletonObject = singletonFactory.getObject();
// 标记为新的单例对象
newSingleton = true;
} catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
} catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
} finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
// 7.创建单例后的操作
//bean创建完成后singletonsCurrentlyInCreation要删除该bean
afterSingletonCreation(beanName);
}
if (newSingleton) {
// 8.如果是新的单例对象,将beanName和对应的bean实例添加到缓存中(singletonObjects、registeredSingletons)
//创建对象成功时,把对象缓存到singletonObjects缓存中,bean创建完成时放入一级缓存
addSingleton(beanName, singletonObject);
}
}
// 9.返回创建出来的单例对象
return singletonObject;
}
}

至此,Spring 关于 singleton bean 循环依赖已经分析完毕了。所以我们基本上可以确定 Spring 解决循环依赖的方案了:Spring 在创建 bean 的时候并不是等它完全完成,而是在创建过程中将创建中的 bean 的 ObjectFactory 提前曝光(即加入到 singletonFactories 缓存中),这样一旦下一个 bean 创建的时候需要依赖 bean ,则直接使用 ObjectFactory 的 getObject() 获取了,也就是 getSingleton() 中的代码片段了。

总结

​ 到这里,关于 Spring 解决 bean 循环依赖就已经分析完毕了。最后来描述下就上面那个循环依赖 Spring 解决的过程:首先 A 完成初始化第一步并将自己提前曝光出来(通过 ObjectFactory 将自己提前曝光),在初始化的时候,发现自己依赖对象 B,此时就会去尝试 get(B),这个时候发现 B 还没有被创建出来,然后 B 就走创建流程,在 B 初始化的时候,同样发现自己依赖 C,C 也没有被创建出来,这个时候 C 又开始初始化进程,但是在初始化的过程中发现自己依赖 A,于是尝试 get(A),这个时候由于 A 已经添加至缓存中(一般都是添加至三级缓存 singletonFactories ),通过 ObjectFactory 提前曝光,所以可以通过 ObjectFactory.getObject() 拿到 A 对象,C 拿到 A 对象后顺利完成初始化,然后将自己添加到一级缓存中,回到 B ,B 也可以拿到 C 对象,完成初始化,A 可以顺利拿到 B 完成初始化。到这里整个链路就已经完成了初始化过程了。

评论