Spring的循环依赖

本文简要介绍了循环依赖以及Spring解决循环依赖的过程

一.定义

循环依赖是指对象之间的循环依赖,即2个或以上的对象互相持有对方,最终形成闭环。这里的对象特指单例对象。

 Spring的循环依赖

 

二.表现形式

对象之间的循环依赖主要有两种表现形式:构造函数循环依赖和属性循环依赖。

2-1 构造函数循环依赖

Spring的循环依赖

 

1 public class A {
2     /**
3      * 有参构造函数
4      */
5     public A(B b) {
6         System.out.println(b);
7     }
8 }
1 public class B {
2     /**
3      * 有参构造函数
4      */
5     public B(A a) {
6         System.out.println(a);
7     }
8 }

A, B,之间形成的循环依赖,具体表现在:A构造函数的参数是B,B构造函数的参数是A,即构造函数循环依赖。

 

将这2个bean交给Spring容器管理,并使用构造函数注入来模拟构造函数循环依赖。

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans
 5         https://www.springframework.org/schema/beans/spring-beans.xsd">
 6 
 7     <bean id="a" class="com.hutao.model.A">
 8         <constructor-arg name="b" ref="b"/>
 9     </bean>
10 
11     <bean id="b" class="com.hutao.model.B">
12         <constructor-arg name="a" ref="a"/>
13     </bean>
14 
15 </beans>

 

测试构造函数循环依赖:

1 public class Test {
2     public static void main(String[] args) {
3         ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
4         A a = context.getBean("a", A.class);
5     }
6 }

测试结果:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
//......

这种构造函数循环依赖无法解决,因为JVM在对类进行实例化时,需要先实例化构造函数的参数,而由于各个构造函数的参数之间是循环依赖的,所以各个参数都无法提前实例化,故只能抛出错误。

 

2-2 属性循环依赖

Spring的循环依赖

 

1 public class A {
2     private B b;
3 
4     public void setB(B b) {
5         this.b = b;
6     }
7 }
1 public class B {
2     private A a;
3 
4     public void setC(A a) {
5         this.a = a;
6     }
7 }

A, B,之间形成的循环依赖,具体表现在:A一个属性是B,B一个属性是A,即属性循环依赖。

 

将这2个bean交给Spring容器管理,并使用属性注入来模拟属性循环依赖。

 1 -<?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans
 5         https://www.springframework.org/schema/beans/spring-beans.xsd">
 6 
 7     <bean id="a" class="com.hutao.model.A">
 8         <property name="b" ref="b"/>
 9     </bean>
10 
11     <bean id="b" class="com.hutao.model.B">
12         <property name="a" ref="a"/>
13     </bean>
14 
15 </beans>

 

测试属性循环依赖:

1 public class Test {
2     public static void main(String[] args) {
3         ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
4         A a = context.getBean("a", A.class);
5         System.out.println(a);
6     }
7 }

测试结果:

 

com.hutao.model.A@2d928643

Process finished with exit code 0

 

这种表现形式的循环依赖Spring可以解决,大致的思路是:Spring先使用各个类的无参构造函数实例化各个对象,再使用Setter去设置对象的属性。但Spring在实例化各个对象时并不是按定义顺序去实例化的,中间还穿插了递归实例化的过程。

 

假定有A ,B, C三个类,A, C之间存在着循环依赖关系(即A有个C类型的属性,C有个A类型的属性),XML配置文件中定义的顺序为A->B->C。

Spring的循环依赖

1 <bean id="a" class="com.hutao.model.A">
2     <property name="c" ref="c"/>
3 </bean>
4 
5 <bean id="b" class="com.hutao.model.B"/>
6 
7 <bean id="c" class="com.hutao.model.C">
8     <property name="a" ref="a"/>
9 </bean>

Spring首先会去实例化A,A实例化完成后,为A注入其属性C的值,在注入属性C的时候发现C还未被实例化就先去实例化C,实例化C完成后,为C注入属性A,而此时A已经完成实例化了,所以可以直接为C注入属性A,然后完成对A中的C属性注入,最后再去实例化B。所以实例化顺序是A-> C->B而不是A->B->C。

 

三.如何检测出是循环依赖

在某个对象创建的过程中,如果递归调用回来发现自己正在创建过程中的话,即说明存在循环依赖。

 

 

四. 怎么解决循环依赖

Spring解决循环依赖的主要依据是Java的引用传递:当我们获取到对象的引用时,对象的属性可以是还未设置的(允许延后设置属性),但构造器的执行必须在获取引用之前。

 

Spring单例对象的初始化主要分为3步:

Spring的循环依赖

 

 

第1步 实例化:调用对象的构造函数实例化对象;

第2步 注入属性:对实例化对象的属性值进行注入;

第3步 初始化:调用SpringXML中的init()方法。

 

由上述初始化步骤可知,循环依赖主要发生在第1,2 步,即:构造器循环依赖和属性循环依赖。Spring解决属性循环依赖使用了三级缓存。

 

 

五.Spring中源码实现

Spring版本信息为:

<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.8.RELEASE</version>
<scope>compile</scope>

 

5-1 三级缓存

三级缓存的源码(调换了顺序):

1 /** Cache of singleton objects: bean name to bean instance. */
2 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
3 ​
4 /** Cache of early singleton objects: bean name to bean instance. */
5 private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
6 ​
7 /** Cache of singleton factories: bean name to ObjectFactory. */
8 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

 

三级缓存:

一级缓存->singletonObjects:存放的是初始化之后的单例对象

二级缓存->earlySingletonObjects:存放的是一个已完成实例化未完成初始化的早期单例对象

三级缓存->singletonFactories:存放的是可产生单例对象的工厂对象(此缓存中的bean name和二级缓存中的bean name是互斥的,即此级缓存中有某个bean的工厂,二级缓存中就没有这个bean)

 

5-2 具体实现

对于非懒加载的类,是在refresh方法中的 finishBeanFactoryInitialization(beanFactory) 方法完成的包扫描以及bean的初始化。

 1 public void refresh() throws BeansException, IllegalStateException {
 2     synchronized (this.startupShutdownMonitor) {
 3         //...
 4 ​
 5         // Instantiate all remaining (non-lazy-init) singletons.
 6         finishBeanFactoryInitialization(beanFactory);
 7 ​
 8         //...
 9     }
10 }

 

 1 /**
 2  * Finish the initialization of this context's bean factory,
 3  * initializing all remaining singleton beans.
 4  */
 5 protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
 6     //...
 7 
 8     // Instantiate all remaining (non-lazy-init) singletons.
 9     beanFactory.preInstantiateSingletons();
10 }

此处调用了beanFactory的一个方法preInstantiateSingletons(),此处的beanFactory是DefaultListableBeanFactory。

 

DefaultListableBeacFactory的preInstantiateSingletons()方法:

 1 public void preInstantiateSingletons() throws BeansException {
 2 
 3     // Iterate over a copy to allow for init methods which in turn register new bean definitions.
 4     // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
 5     List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
 6 
 7     // Trigger initialization of all non-lazy singleton beans...
 8     for (String beanName : beanNames) {
 9         RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
10         //判断Bean是否为非抽象类、单例、非懒加载,如果都满足才初始化
11         if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
12             if (isFactoryBean(beanName)) {
13                 //无关代码,对FactoryBean的处理
14             }
15             else {
16                 //这里就是对普通Bean的初始化
17                 getBean(beanName);
18             }
19         }
20     }
21 
22     //...
23 }

在此方法中,循环Spring容器中的所有Bean,依次对其进行初始化,初始化的入口就是getBean()方法。

 

追踪getBean方法:

1 //AbstractBeanFactory.java
2 
3 @Override
4 public Object getBean(String name) throws BeansException {
5     return doGetBean(name, null, null, false);
6 }

引用了AbstractBeanFacotry中重载的doGetBean()方法:

 

追踪doGetBean()方法:

 1 protected <T> T doGetBean(
 2     String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
 3     throws BeansException {
 4 
 5     String beanName = transformedBeanName(name);
 6     Object bean;
 7 
 8     //方法① 从三个缓存(map)中获取单例bean
 9     // Eagerly check singleton cache for manually registered singletons.
10     Object sharedInstance = getSingleton(beanName);
11     if (sharedInstance != null && args == null) {
12         if (logger.isTraceEnabled()) {
13             if (isSingletonCurrentlyInCreation(beanName)) {
14                 logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
15                              "' that is not fully initialized yet - a consequence of a circular reference");
16             }
17             else {
18                 logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
19             }
20         }
21         bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
22     }
23 
24     else {
25         //如果是多例的循环依赖,直接抛出异常
26         // Fail if we're already creating this bean instance:
27         // We're assumably within a circular reference.
28         if (isPrototypeCurrentlyInCreation(beanName)) {
29             throw new BeanCurrentlyInCreationException(beanName);
30         }
31 
32         //...
33 
34         try {
35             RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
36             
37             //...
38 
39             // Create bean instance.
40             if (mbd.isSingleton()) {
41                 //方法② 获取单例bean
42                 sharedInstance = getSingleton(beanName, () -> {
43                     try {
44                         //方法③ 创建bean
45                         return createBean(beanName, mbd, args);
46                     }
47                     catch (BeansException ex) {
48                         // Explicitly remove instance from singleton cache: It might have been put there
49                         // eagerly by the creation process, to allow for circular reference resolution.
50                         // Also remove any beans that received a temporary reference to the bean.
51                         destroySingleton(beanName);
52                         throw ex;
53                     }
54                 });
55                 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
56             }
57         }
58         catch (BeansException ex) {
59             cleanupAfterBeanCreationFailure(beanName);
60             throw ex;
61         }
62     }
63 
64     //...
65     
66     return (T) bean;
67 }

该方法中使用的方法① 、方法②、方法③对解决循环依赖起了至关重要的作用,下面来依次分析:

 

方法①:getSingleton(String beanName, boolean allowEarlyReference)

 1 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
 2     //步骤A:先从一级缓存singleObjects中根据beanName获取bean
 3     Object singletonObject = this.singletonObjects.get(beanName);
 4     
 5     if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
 6         synchronized (this.singletonObjects) {
 7             //步骤B:如果从一级缓存中未获取到bean,则再根据beanName从二级缓存earlySingletonObjects中获取
 8             singletonObject = this.earlySingletonObjects.get(beanName);
 9             if (singletonObject == null && allowEarlyReference) {
10                 
11                 //步骤C:如果从二级缓存中未获取到bean,则再根据beanName从三级缓存singletonFactories中获取工厂,然后通过工厂获取对应的bean
12                 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
13                 if (singletonFactory != null) {
14                     singletonObject = singletonFactory.getObject();
15                     this.earlySingletonObjects.put(beanName, singletonObject);
16                     this.singletonFactories.remove(beanName);
17                 }
18             }
19         }
20     }
21     return singletonObject;
22 }

通过上面的步骤可以看出这三级缓存的优先级。其中singletonObjects里面存放的是初始化之后的单例对象;earlySingletonObjects中存放的是一个已完成实例化未完成初始化的早期单例对象;而singletonFactories中存放的是ObjectFactory对象,此对象的getObject方法返回值即刚完成实例化还未开始初始化的单例对象。所以先后顺序是,单例对象先存在于singletonFactories中,后存在于earlySingletonObjects中,最后初始化完成后放入singletonObjects中。

当debug到此处时,以2-2中的A, B为例,第一步走到这个方法的是A,此时从这三个缓存中获取到的都是null,因为还没有内容被添加到这三级缓存中去。这个方法主要是给循环依赖中后过来的对象使用。

 

 

方法②:getSingleton(String beanName, ObjectFactory<?> singletonFactory)

 1 public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
 2     Assert.notNull(beanName, "Bean name must not be null");
 3     synchronized (this.singletonObjects) {
 4         Object singletonObject = this.singletonObjects.get(beanName);
 5         if (singletonObject == null) {
 6             //...
 7         
 8             //步骤A
 9             beforeSingletonCreation(beanName);
10             boolean newSingleton = false;
11             //...
12             try {
13                 //步骤B
14                 singletonObject = singletonFactory.getObject();
15                 newSingleton = true;
16             }
17             //...
18             finally {
19                 if (recordSuppressedExceptions) {
20                     this.suppressedExceptions = null;
21                 }
22                 //步骤C
23                 afterSingletonCreation(beanName);
24             }
25             if (newSingleton) {
26                 //步骤D
27                 addSingleton(beanName, singletonObject);
28             }
29         }
30         return singletonObject;
31     }
32 }

获取单例对象的主要逻辑就是次方法实现的,主要分为上面4个步骤:

 

步骤A:

 1 /**
 2  * Callback before singleton creation.
 3  * <p>The default implementation register the singleton as currently in creation.
 4  * @param beanName the name of the singleton about to be created
 5  * @see #isSingletonCurrentlyInCreation
 6  */
 7 protected void beforeSingletonCreation(String beanName) {
 8     if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
 9         throw new BeanCurrentlyInCreationException(beanName);
10     }
11 }

首次将beanName放入到singletonCurrentlyInCreation中

 

步骤C:

 1 /**
 2  * Callback after singleton creation.
 3  * <p>The default implementation marks the singleton as not in creation anymore.
 4  * @param beanName the name of the singleton that has been created
 5  * @see #isSingletonCurrentlyInCreation
 6  */
 7 protected void afterSingletonCreation(String beanName) {
 8     if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
 9         throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
10     }
11 }

得到单例对象后,再讲beanName从singletonsCurrentlyInCreation中移除

 

步骤D:

 1 /**
 2  * Add the given singleton object to the singleton cache of this factory.
 3  * <p>To be called for eager registration of singletons.
 4  * @param beanName the name of the bean
 5  * @param singletonObject the singleton object
 6  */
 7 protected void addSingleton(String beanName, Object singletonObject) {
 8     synchronized (this.singletonObjects) {
 9         //添加所创建的单例bean到一级缓存中
10         this.singletonObjects.put(beanName, singletonObject);
11         //从三级缓存中移出,此缓存在解决循环依赖的过程中起了关键作用
12         this.singletonFactories.remove(beanName);
13         //从二级缓存中移出
14         this.earlySingletonObjects.remove(beanName);
15         //添加bean到已注册的单例名字集合中
16         this.registeredSingletons.add(beanName);
17     }
18 }

 

步骤B:

此处调用了ObjectFactory的getObject方法,此方法是在方法③中实现的

Spring的循环依赖

 

步骤B其实也就是方法③

 

方法③:createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)

 1 /**
 2  * Central method of this class: creates a bean instance,
 3  * populates the bean instance, applies post-processors, etc.
 4  * @see #doCreateBean
 5  */
 6 @Override
 7 protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
 8     throws BeanCreationException {
 9     
10     //...
11 
12     try {
13         Object beanInstance = doCreateBean(beanName, mbdToUse, args);
14         //...
15         return beanInstance;
16     }
17     
18     //...
19 }

去掉无关代码后,关键方法只有doCreateBean() 方法

 

doCreateBean() 方法:

 1 /**
 2  * Actually create the specified bean. Pre-creation processing has already happened
 3  * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
 4  * <p>Differentiates between default bean instantiation, use of a
 5  * factory method, and autowiring a constructor.
 6  * @param beanName the name of the bean
 7  * @param mbd the merged bean definition for the bean
 8  * @param args explicit arguments to use for constructor or factory method invocation
 9  * @return a new instance of the bean
10  * @throws BeanCreationException if the bean could not be created
11  * @see #instantiateBean
12  * @see #instantiateUsingFactoryMethod
13  * @see #autowireConstructor
14  */
15 protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
16     throws BeanCreationException {
17 
18     // Instantiate the bean.
19     BeanWrapper instanceWrapper = null;
20     if (mbd.isSingleton()) {
21         instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
22     }
23     if (instanceWrapper == null) {
24         //实例化bean(4.中单例对象初始化步骤1)
25         instanceWrapper = createBeanInstance(beanName, mbd, args);
26     }
27     Object bean = instanceWrapper.getWrappedInstance();
28     
29     //...
30 
31     // Eagerly cache singletons to be able to resolve circular references
32     // even when triggered by lifecycle interfaces like BeanFactoryAware.
33     boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
34                                       isSingletonCurrentlyInCreation(beanName));
35     if (earlySingletonExposure) {
36         if (logger.isTraceEnabled()) {
37             logger.trace("Eagerly caching bean '" + beanName +
38                          "' to allow for resolving potential circular references");
39         }
40         // ★重要:将实例化的bean添加到三级缓存singletonFactories中
41         addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
42     }
43 
44     // Initialize the bean instance.
45     Object exposedObject = bean;
46     try {
47         //为实例化的bean注入属性(4.中单例对象初始化步骤2)
48         populateBean(beanName, mbd, instanceWrapper);
49         //初始化bean(4.中单例对象初始化步骤3)
50         exposedObject = initializeBean(beanName, exposedObject, mbd);
51     }
52     
53     //...
54     
55     return exposedObject;
56 }

上面代码中[★重要]标注的重点是此方法的关键。在addSingletonFacotry()方法中,将< beanName->能创建该bean的工厂>键值对添加到了三级缓存中以供其他对象依赖时调用。

populateBean()方法对刚实例化的bean进行属性注入,如果遇到要注入的属性对应的值是由Spring管理的bean,则再通过getBean方法获取该bean。

 

5-3 总结

属性注入主要是在populateBean() 方法中进行的。对于循环依赖,以2-2中的A, B为例,假定Spring的加载顺序为先加载A。其具体的加载过程为:

  1. 首先触发getBean() 方法对A进行初始化;

  2. 然后走到方法①,此时三级缓存都为空,获取不到实例;

  3. 走到方法②,其中的步骤B触发对方法③的调用,即是对doCreateBean() 方法的调用;

  4. 在方法③中,先通过createBeanInstance实例化A对象,然后将该实例通过addSingletonFactory放入singletonFactories中,完成A对象的早期暴露;

  5. 在方法③中,通过populateBean() 方法对A对象的属性进行注入,发现它有一个B属性,则触发getBean() 对B进行初始化(递归);

  6. 重复步骤3、4、5,只是此时要初始化的是B对象;

  7. 再次走到步骤5时,调用populateBean() 方法对B对象进行属性注入,发现它有一个A属性,则触发getBean() 对A进行初始化;

  8. 对A进行初始化,又来到步骤2,此时第三级缓存中已经有了A的信息了,因为在步骤4时,就已经将A实例放入到了singletonFacotries(第三级缓存中)了,此时可直接得到A的实例并返回;

  9. 完成对B的初始化,继而往上回溯完成对A的B属性注入及初始化。

缓存 开始时 实例化对象A后 为A注入属性B,调用getBean初始化B,在实例化B后 为B注入属性A,调用getBean(String, Boolean)后 B初始化完成后 A初始化完成后
一级缓存(singletonObjects)         B BA
二级缓存(earlySingletonObjects)       A A  
三级缓存(singletonFactories)   A AB B    

 

参考源:

1. https://www.cnblogs.com/leeego-123/p/12165278.html

2. https://blog.csdn.net/qq_36381855/article/details/79752689

上一篇:第 4 章 Spring


下一篇:5.2 spring5源码--spring AOP源码分析三---切面源码分析