Spring是如何解决Bean的循环依赖问题的?

相信使用过Spring4及以下版本的各位一定碰到过这个问题,在Spring早期循环依赖一直成为我们的一块心病只要稍微调用粗心一些循环调用就会无法启动,下面我们分析下Spring是如何解决这个问题的!

定义

循环依赖顾名思义就是循环引用,两个bean互相持有对方的引用。

如何解决循环依赖

Spring的单例对象的初始化主要分为三步:
(1)createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象
(2)populateBean:填充属性,这一步主要是多bean的依赖属性进行填充
(3)initializeBean:调用spring xml中的init 方法。

从上面讲述的单例bean初始化步骤我们可以知道,循环依赖主要发生在第一、第二部。也就是构造器循环依赖和field循环依赖。

那么我们要解决循环引用也应该从初始化过程着手,对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,Spring为了解决单例的循环依赖问题,使用了三级缓存。

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
// DefaultSingletonBeanRegistry.java

/**
* Cache of singleton objects: bean name to bean instance.
*
* 存放的是单例 bean 的映射。
*
* 对应关系为 bean name --> bean instance
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/**
* Cache of singleton factories: bean name to ObjectFactory.
*
* 存放的是【早期】的单例 bean 的映射。
*
* 对应关系也是 bean name --> bean instance。
*
* 它与 {@link #singletonObjects} 的区别区别在,于 earlySingletonObjects 中存放的 bean 不一定是完整的。
*
* 从 {@link #getSingleton(String)} 方法中,中我们可以了解,bean 在创建过程中就已经加入到 earlySingletonObjects 中了,
* 所以当在 bean 的创建过程中就可以通过 getBean() 方法获取。
* 这个 Map 也是解决【循环依赖】的关键所在。
**/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/**
* Cache of early singleton objects: bean name to bean instance.
*
* 存放的是 ObjectFactory 的映射,可以理解为创建单例 bean 的 factory 。
*
* 对应关系是 bean name --> ObjectFactory
*/
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
  • singletonObjects :单例对象的 Cache 。

  • singletonFactories : 单例对象工厂的 Cache 。

  • earlySingletonObjects :提前曝光的单例对象的 Cache 。
    它们三,就是 Spring 解决 singleton bean 的关键因素所在,我称他们为三级缓存:

    第一级为 singletonObjects
    第二级为 earlySingletonObjects
    第三级为 singletonFactories

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
 // org/springframework/beans/factory/support/DefaultSingletonBeanRegistry

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 从单例的beanMap中查询获取bean(一级缓存)
Object singletonObject = this.singletonObjects.get(beanName);
// 如果bean未查询到,并且bean是正在创建中的
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 加锁操作
synchronized (this.singletonObjects) {
// 从提前加载的bean map中获取单例bean(从二级缓存中获取)
//二级缓存的作用就是存储未完全实例化的bean对象
singletonObject = this.earlySingletonObjects.get(beanName);
//如果bean不存在且允许提前使用bean引用,否则方法直接返回获取到的内容
if (singletonObject == null && allowEarlyReference) {
// 从单例bean工厂获取某个bean的工厂
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//如果生成成功则将bean保存到二级缓存中,并从三级缓存中删除
//这样,就从三级缓存升级到二级缓存了。
//所以,二级缓存存在的意义,就是缓存三级缓存中的 ObjectFactory 的 #getObject() 方法的执行结果,提早曝光的单例 Bean 对象。
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}

addSingletonFactory

上面是讲如何从缓存中获取bean,那到底bean从哪里添加进去呢,一直深入往下走大家会找到 AbstractAutowireCapableBeanFactory#doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) 方法,这段代码就是将未完全实例化的对象(其实是该对象的对象工厂,但是该对象工厂只会调用一次)添加到三级缓存中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java

// 如果bean是单例的,且允许循环引用,且bean正在创建中
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
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 factory 添加到三级缓存中,后续用来调用提前暴露bean
// 重点!这里是为了后期避免循环依赖
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

#addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) 方法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
  // org.springframework.beans.factory.support.DefaultSingletonBeanRegistry

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
// 如果bean没有创建完成,则将对象工厂存放到三级缓存中
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}

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

addSingleton

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

1
2
3
4
5
6
7
8
9
10
11
// org/springframework/beans/factory/support/DefaultSingletonBeanRegistry

protected void addSingleton(String beanName, Object singletonObject) {
//添加至一级缓存,同时从二级、三级缓存中删除。
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}

这个方法在我们创建 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
 // org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
//....
try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
//.....
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}

结束

至此,Spring的处理单例bean循环引用的流程就浮出水面了。所以我们基本上可以确定 Spring 解决循环依赖的方案了:

  • Spring 在创建 bean 的时候并不是等它完全完成,而是在创建过程中将创建中的 bean 的 ObjectFactory 提前曝光(即加入到 singletonFactories 缓存中)。

  • 这样,一旦下一个 bean 创建的时候需要依赖 bean ,则直接使用 ObjectFactory 的 #getObject() 方法来获取了。

    到这里,关于 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 完成初始化。到这里整个链路就已经完成了初始化过程了

    一张图解释,可以极大帮助大家理解处理流程(很重要):