spring中循环依赖的处理

本质上,spring 中通过提前暴露来解决循环依赖。

什么是循环依赖?

java 中的循环依赖分为两种,一种是构造器的循环依赖。另一种是属性的循环依赖。

构造器的循环依赖就是在构造方法中有属性循环依赖。
这种循环依赖无法解决,因为 JVM 虚拟机在对类进行实例化的时候,需要先实例构造器的参数,由于循环引用这个参数无法实例化,只能抛出错误。

属性的循环依赖,在类的属性中具有循环依赖。

循环依赖与属性注入

  1. 对于非懒加载的类,实在 refresh 方法中的finishBeanFactoryInitialization(beanFactory)完成的包扫描和 bean 初始化。在此方法中调用的 beanFactory 的 preInstantiateSingletons 方法。
1
2
3
4
5
6
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 其他代码

// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}
  1. preInstantiateSingletons 方法,可以看到就是在此方法中循环 Spring 容器中的所有 bean,一次对其进行初始化,入口方法就是 getBean()。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void preInstantiateSingletons() throws BeansException {

List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...

for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// 判断为非抽象类、是单例、非懒加载 才给初始化
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
// 无关代码(针对FactoryBean的处理)
} else {
// 重要!!!普通bean就是在这里初始化的
getBean(beanName);
}
}
}

// 其他无关代码
}
  1. 追踪 getBean 方法,继续查看 doGetBean 方法
1
2
3
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
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
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;

// 方法1)从三个map中获取单例类
if(...) {
Object sharedInstance = getSingleton(beanName);
// 省略无关代码
} else {
// 如果是多例的循环引用,则直接报错
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 省略若干无关代码
try {
// Create bean instance.
if (mbd.isSingleton()) {
// 方法2) 获取单例对象
sharedInstance = getSingleton(beanName, () -> {
//方法3) 创建ObjectFactory中getObject方法的返回值
try {
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;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
// 省略若干无关代码
return (T) bean;
}
}
  1. 方法一 getSingleton(String beanName,boolean allowEarlyReference)方法。通过下面的方法可以看出这几个 map 的优先级:
    1. singletonObjects:单例对象的缓存,存放的是初始化后的对象。
    2. earlySingletonObjects:提前曝光的单例对象的缓存,存放的是一个已完成实例化未完成初始化的早期单例对象。
    3. singletonFactories:单例对象工厂的缓存,存放的是 ObjectFactory 对象,此对象的 getObject 方法返回值即刚完成实例化还未开始初始化的单例对象。

所以先后顺序是,单例对象先存在于 singletonFactories 中,后存在于 earlySingletonObjects 中,最后初始化完成后放入 singletonObjects 中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);// 步骤A
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);// 步骤B
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);// 步骤C
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
  1. 方法二 getSingleton(String beanName,ObjectFactory<?> singletonFactory)方法。这个方法与第一个方法相同。内部逻辑不同,为重载方法。获取单例的主要逻辑就是此方法实现的。
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
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) {
// 省略无关代码
beforeSingletonCreation(beanName); // 步骤A
boolean newSingleton = false;
// 省略无关代码
try {
singletonObject = singletonFactory.getObject();// 步骤B
newSingleton = true;
}
// 省略无关代码
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);// 步骤C
}
if (newSingleton) {
addSingleton(beanName, singletonObject);// 步骤D
}
}
return singletonObject;
}
}

此方法中分为四个步骤,

  1. A 步骤
1
2
3
4
5
6
protected void beforeSingletonCreation(String beanName) {
// 判断,并首次将beanName即teacher放入singletonsCurrentlyInCreation中
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
  1. C 步骤
1
2
3
4
5
6
protected void afterSingletonCreation(String beanName) {
// 得到单例对象后,再讲beanName从singletonsCurrentlyInCreation中移除
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
}
  1. D 步骤
1
2
3
4
5
6
7
8
9
10
11
12
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
//添加单例对象到map中
this.singletonObjects.put(beanName, singletonObject);
//从早期暴露的工厂中移除,此map在解决循环依赖中发挥了关键的作用
this.singletonFactories.remove(beanName);
//从早期暴露的对象map中移除
this.earlySingletonObjects.remove(beanName);
//添加到已注册的单例名字集合中
this.registeredSingletons.add(beanName);
}
}
  1. B 步骤,此处调用了 ObjectFactory 的 getObject 方法,返回值就是方法三中的 createBean 方法的返回值,即方法三返回我们需要的单例对象。
1
2
3
4
5
6
7
8
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
// 省略无关代码
try {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
return beanInstance;
}
// 省略无关代码
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
BeanWrapper instanceWrapper = null;
// 省略代码
if (instanceWrapper == null) {
// 实例化bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// 重点!!!将实例化的对象添加到singletonFactories中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// 初始化bean
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);//也很重要
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
// 省略无关代码
return exposedObject;
}

在 addSingletonFactory 方法中,将第二个参数 ObjectFactory 存入了 singletonFactories 供其他对象依赖时调用。然后下面的populateBean 方法对刚实例化的 bean 进行属性注入(该方法关联较多,本文暂时不展开追踪了,有兴趣的园友自行查看即可),如果遇到 Spring 中的对象属性,则再通过 getBean 方法获取该对象。至此,循环依赖在 Spring 中的处理过程已经追溯完毕。


总结

属性注入主要是在 populateBean 方法中进行的。对于循环依赖,以我们上文中的 Teacher 中注入了 Student、Student 中注入了 Teacher 为例来说明,假定 Spring 的加载顺序为先加载 Teacher,再加载 Student。
getBean 方法触发 Teacher 的初始化后:

  1. 首先走到 3 中的方法 1),此时 map 中都为空,获取不到实例;
  2. 然后走到方法 2)中,步骤 A、步骤 C、步骤 D 为控制 map 中数据的方法,实现简单,可暂不关注。其中步骤 B 的 getObject 方法触发对方法 3)的调用;
  3. 在方法 3)中,先通过 createBeanInstance 实例化 Teacher 对象,又将该实例化的对象通过 addSingletonFactory 方法放入 singletonFactories 中,完成 Teacher 对象早期的暴露;
  4. 然后在方法 3)中通过 populateBean 方法对 Teacher 对象进行属性的注入,发现它有一个 Student 属性,则触发 getBean 方法对 Student 进行初始化
  5. 重复 a、b、c 步骤,只是此时要初始化的是 Student 对象
  6. 走到 d 的时候,调用 populateBean 对 Student 对象进行属性注入,发现它有一个 Teacher 属性,则触发 getBean 方法对 Teacher 进行初始化;
  7. 对 Teacher 进行初始化,又来到 a,但此时 map 已经不为空了,因为之前在 c 步骤中已经将 Teacher 实例放入了 singletonFactories 中,a 中得到 Teacher 实例后返回;
  8. 完成 f 中对 Student 的初始化,继而依次往上回溯完成 Teacher 的初始化;

完成 Teacher 的初始化后,Student 的初始化就简单了,因为 map 中已经存了这个单例。
至此,Spring 循环依赖的总结分析结束,一句话来概括一下:Spring 通过将实例化后的对象提前暴露给 Spring 容器中的 singletonFactories,解决了循环依赖的问题。


理解

A 依赖 B,B 依赖 A。
构造 A 时,从一级缓存中获取不到,从二级缓存中获取不到,从三级缓存获取一个不完整的,提前曝光的 A,并且放入二级缓存,A 属性注入需要构造 B,在构造 B 时,可以从二级缓存获取 A,完成 B 的属性注入,返回 A,在完成 A 的属性注入。
Spring 中的循环依赖解决详解 - 不死码农 - 博客园