Spring循环依赖问题

126 阅读3分钟

一、什么是循环依赖

首先,我们设想有这么一个场景,有TestA、TestB、TestC三个类,TestA依赖注入TestB,TestB依赖注入TestC,TestC又依赖注入TestA,在实例化Bean时就会构成了循环依赖。

当我们希望向一个Bean里注入另外一个Bean时,

通常有两种方式:

(1)构造器注入

(2)setter()注入

那么相应的Spring的循环依赖也包含了两种情况:

(1)构造器循环依赖

(2)  setter方法循环依赖,其中又分为单例循环依赖和多例循环依赖

然后,我们要介绍另外一个名词,“当前创建Bean池”,这是在Spring中用于存放正在创建的Bean的数据结构,用于检查循环依赖的。

二、构造器循环依赖

首先它是无法被解决的,Spring中出现了这种构造器循环依赖的处理方式就是报错。

为什么无法解决呢?我们推演一下TestA,TestB,TestC的创建流程。

1、创建TestA,首先去“当前创建Bean池”中查找是否存在TestA的标识符,没有找到,通过构造方法创建对象,发现构造方法需要TestB作为参数,创建TestB,并且把TestA的标识符放入“当前创建Bean池”。

2、创建TestB,首先去“当前创建Bean池”中查找是否存在TestB的标识符,没有找到,通过构造方法创建对象,发现构造方法需要TestC作为参数,创建TestC,并且把TestB的标识符放入“当前创建Bean池”。

3、创建TestC,首先去“当前创建Bean池”中查找是否存在TestC的标识符,没有找到,通过构造方法创建对象,发现构造方法需要TestA作为参数,创建TestA,并且把TestC的标识符放入“当前创建Bean池”。

4、创建TestA,首先去“当前创建Bean池”中查找是否存在TestA的标识符,卧槽,找到了,有循环依赖,报错。

三、单例setter循环依赖

这种循环依赖是可以被解决的。Spring的解决方式就是在初始化Bean的时候,通过ObjectFactory工厂单例方法提前曝光已经创建但是还没设置属性的Bean。同样是上述的例子。

1、创建TestA,首先去“当前创建Bean池”中查找是否存在TestA的标识符,没有找到,通过无参构造方法创建实例,通过ObjectFactory提前曝光正在创建的TestA,把TestA标识符放入“当前创建Bean池”,通过setter方法注入TestB,调用getBean()获取TestB实例,由于还没创建,创建TestB。

2、创建TestB,首先去“当前创建Bean池”中查找是否存在TestB的标识符,没有找到,通过无参构造方法创建实例,通过ObjectFactory提前曝光正在创建的TestB,把TestB标识符放入“当前创建Bean池”,通过setter方法注入TestC,调用getBean()获取TestC实例,由于还没创建,创建TestC。

3、创建TestC,首先去“当前创建Bean池”中查找是否存在TestC的标识符,没有找到,通过无参构造方法创建实例,通过ObjectFactory提前曝光正在创建的TestC,把TestC标识符放入“当前创建Bean池”,通过setter方法注入TestA,调用getBean()获取TestA实例,发现有提前曝光的ObjectFactory,进而通过ObjectFactory.getObject()获取TestA实例,完成TestC的属性注入。

4、由于TestC已经完成属性注入创建完毕,TestB也可以成功注入TestC,TestA也可以成功注入TestB,至此TestA,TestB,TestC创建完毕。    

四、prototype作用域setter循环依赖

无法被解决,因为每次都是获取新创建的Bean实例,Spring不缓存prototype作用域的Bean,无法通过ObjectFactory提前暴露一个正在创建的bean。