Spring的循环依赖
阅读原文时间:2021年07月18日阅读:1

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

一.定义

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

二.表现形式

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

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
2 http://www.springframework.org/schema/beans 5 https://www.springframework.org/schema/beans/spring-beans.xsd"> 6 7 8 9 10 11 12 13 14 15

测试构造函数循环依赖:

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在对类进行实例化时,需要先实例化构造函数的参数,而由于各个构造函数的参数之间是循环依赖的,所以各个参数都无法提前实例化,故只能抛出错误。

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 -
2 http://www.springframework.org/schema/beans 5 https://www.springframework.org/schema/beans/spring-beans.xsd"> 6 7 8 9 10 11 12 13 14 15

测试属性循环依赖:

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。

1 2 3
4
5
6
7 8 9

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步:

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

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

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

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

五.Spring中源码实现

Spring版本信息为:

org.springframework
spring-context
5.2.8.RELEASE
compile

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

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

三级缓存:

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

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

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

对于非懒加载的类,是在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 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 doGetBean(
2 String name, @Nullable Class 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 *

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 *

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 *

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方法,此方法是在方法③中实现的

步骤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 *

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。

属性注入主要是在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