导航菜单
首页 >  万字长文带你彻底吃透Spring循环依赖堪称全网最全文末  > 万字长文带你彻底吃透Spring循环依赖

万字长文带你彻底吃透Spring循环依赖

一、学习指引

Spring中的循环依赖问题,你真的彻底了解过吗?

Spring的循环依赖问题可以说是面试过程中出现的非常频繁的问题。比如,在面试过程中面试官可能会问:有了解过Spring中的循环依赖问题吗?或者会问:什么是循环依赖问题?或者会问:Spring中在哪些场景下会产生循环依赖问题?或者会问:Spring是如何解决循环依赖问题的?看似轻描淡写的一句话,实际要考察的内容也是比较多的。面试官可以通过这个问题考察面试者是否研究过Spring的源码,有没有了解过Spring中的循环依赖问题。

文末扫码加星球立减¥200,仅限2023年12月,仅限前300名,先到先得。

本章,我们就一起来聊聊Spring中的循环依赖问题。

二、循环依赖概述

什么是循环依赖?

循环依赖其实也很好理解,可以将这个词拆分成两部分,一个是循环,一个是依赖。循环,顾名思义就是指形成了一个闭合环路,也就是闭环。依赖就是指某个事件的发生要依赖另一个事件。

在Spring中的循环依赖就是指一个或者多个Bean之间存在着互相依赖的关系,并且形成了循环调用。例如,在Spring中,A依赖B,B又依赖A,A和B之间就形成了相互依赖的关系。创建A对象时,发现A对象依赖了B对象,此时先去创建B对象。创建B对象时,发现B对象又依赖了A对象,此时又去创建A对象。创建A对象时,发现A对象依赖了B对象....如果Spring不去处理这种情况,就会发生死循环,一直会创建A对象和B对象,直到抛出异常为止。

同理,在Spring中多个对象之间也有可能存在循环依赖,例如,A依赖B,B依赖C,C又依赖A,A、B、C之间形成了互相依赖的关系,这也是一种循环依赖。

三、循环依赖类型

循环依赖有这些类型呢?

循环依赖总体上可以分成:自我依赖、直接依赖和间接依赖三种类型,如图20-1所示。

万字长文带你彻底吃透Spring循环依赖

3.1 自我依赖

自我依赖就是自己依赖自己,从而形成的循环依赖,一般情况下不会发生这种循环依赖,如图20-2所示。

万字长文带你彻底吃透Spring循环依赖

3.2 直接依赖

直接依赖一般是发生在两个对象之间,例如对象A依赖对象B,对象B又依赖对象A,对象A和对象B之间形成了依赖关系,如图20-3所示。

万字长文带你彻底吃透Spring循环依赖

3.3 间接依赖

间接依赖一般是发生在三个或三个以上对象之间互相依赖的场景,例如对象A依赖对象B,对象B依赖对象C,对象C又依赖对象A,对象A、对象B和对象C之间就形成了循环依赖,如图20-4所示。

万字长文带你彻底吃透Spring循环依赖

四、循环依赖场景

Spring中有哪些循环依赖的场景?

Spring中的循环依赖场景总体上可以分成单例Bean的setter循环依赖、多例Bean的setter循环依赖、代理对象的setter循环依赖、构造方法的循环依赖和DependsOn的循环依赖。如图20-5所示。

万字长文带你彻底吃透Spring循环依赖

4.1 单例Bean的setter循环依赖的特殊情况

Spring是支持基于单例Bean的setter方法的循环依赖的,不过有一种特殊情况需要注意。本节,我们就一起实现基于单例Bean的setter方法的循环依赖的特殊情况,具体实现步骤如下所示。

(1)新增SpecialCircularBeanA类

SpecialCircularBeanA类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.special.bean.SpecialCircularBeanA。

@Componentpublic class SpecialCircularBeanA {@Autowiredprivate SpecialCircularBeanB specialCircularBeanB;@Overridepublic String toString() {return "SpecialCircularBeanA{" +"specialCircularBeanB=" + specialCircularBeanB +'}';}}

可以看到,在SpecialCircularBeanA类上只标注了@Component注解,所以在IOC容器启动时,会在IOC容器中创建SpecialCircularBeanA类型的单例Bean,并且在SpecialCircularBeanA类型的Bean对象中,会依赖SpecialCircularBeanB类型的Bean对象。同时,在SpecialCircularBeanA类中重写了toString()方法,打印了依赖的SpecialCircularBeanB类型的对象。

(2)新增SpecialCircularBeanB类

SpecialCircularBeanB类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.special.bean.SpecialCircularBeanB。

@Componentpublic class SpecialCircularBeanB {@Autowiredprivate SpecialCircularBeanA specialCircularBeanA;@Overridepublic String toString() {return "SpecialCircularBeanB{" +"specialCircularBeanA=" + specialCircularBeanA +'}';}}

可以看到,在SpecialCircularBeanB类上只标注了@Component注解,所以在IOC容器启动时,会在IOC容器中创建SpecialCircularBeanB类型的单例Bean,并且在SpecialCircularBeanB类型的Bean对象中,会依赖SpecialCircularBeanA类型的Bean对象。同时,在SpecialCircularBeanB类中重写了toString()方法,打印了依赖的SpecialCircularBeanA类型的对象。

(3)新增SpecialCircularConfig类

SpecialCircularConfig类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.special.config.SpecialCircularConfig。

@Configuration@ComponentScan(value = {"io.binghe.spring.annotation.chapter20.special"})public class SpecialCircularConfig {}

可以看到,在SpecialCircularConfig类上标注了@Configuration注解,说明SpecialCircularConfig类是案例程序的配置类,并且使用@ComponentScan注解指定了扫描的包。

(4)新增SpecialCircularTest类

SpecialCircularTest类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.special.SpecialCircularTest。

public class SpecialCircularTest {public static void mAIn(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpecialCircularConfig.class);SpecialCircularBeanA specialCircularBeanA = context.getBean(SpecialCircularBeanA.class);System.out.println(specialCircularBeanA);context.close();}}

可以看到,在SpecialCircularTest类的main()方法中,传入SpecialCircularConfig类的Class对象后,创建IOC容器,随后从IOC容器中获取SpecialCircularBeanA类型的Bean对象并进行打印,最后关闭IOC容器。

(5)运行SpecialCircularTest类

运行SpecialCircularTest类的main()方法,输出的结果信息如下所示。

Exception in thread "main" JAVA.lang.StackOverflowError

可以看到,程序抛出了StackOverflowError异常。

其实,从本质上讲,这个异常不是Spring抛出的,而是JVM抛出的栈溢出错误。

4.2 多例Bean的setter循环依赖

Spring是不支持基于多例Bean,也就是原型模式下的Bean的setter方法的循环依赖。本节,我们一起实现一个基于多例Bean的set方法的循环依赖案例,具体实现步骤如下所示。

(1)新增PrototypeCircularBeanA类

PrototypeCircularBeanA类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.prototype.bean.PrototypeCircularBeanA。

@Component@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)public class PrototypeCircularBeanA {@Autowiredprivate PrototypeCircularBeanB prototypeCircularBeanB;public PrototypeCircularBeanB getPrototypeCircularBeanB() {return prototypeCircularBeanB;}}

可以看到,PrototypeCircularBeanA类的对象在Spring中是多例Bean,并且依赖了PrototypeCircularBeanB类型的Bean对象,并提供了getPrototypeCircularBeanB()方法返回PrototypeCircularBeanB类型的Bean对象。

(2)新增PrototypeCircularBeanB类

PrototypeCircularBeanB类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.prototype.bean.PrototypeCircularBeanB。

@Component@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)public class PrototypeCircularBeanB {@Autowiredprivate PrototypeCircularBeanA prototypeCircularBeanA;public PrototypeCircularBeanA getPrototypeCircularBeanA() {return prototypeCircularBeanA;}}

可以看到,PrototypeCircularBeanB类的对象在Spring中是多例Bean,并且依赖了PrototypeCircularBeanA类型的Bean对象,并提供了getPrototypeCircularBeanA()方法返回PrototypeCircularBeanA类型的Bean对象。

(3)新增PrototypeCircularConfig类

PrototypeCircularConfig类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.prototype.config.PrototypeCircularConfig。

@Configuration@ComponentScan(value = {"io.binghe.spring.annotation.chapter20.prototype"})public class PrototypeCircularConfig {}

可以看到,在PrototypeCircularConfig类上标注了@Configuration注解,说明PrototypeCircularConfig类是案例程序的配置类,并且使用@ComponentScan注解指定了要扫描的包名。

(4)新增PrototypeCircularTest类

PrototypeCircularTest类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.prototype.PrototypeCircularTest。

public class PrototypeCircularTest {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PrototypeCircularConfig.class);PrototypeCircularBeanA prototypeCircularBeanA = context.getBean(PrototypeCircularBeanA.class);System.out.println("prototypeCircularBeanA===>>>" + prototypeCircularBeanA);System.out.println("prototypeCircularBeanB===>>>" + prototypeCircularBeanA.getPrototypeCircularBeanB());context.close();}}

可以看到,在PrototypeCircularTest类的main()方法中,创建完IOC容器后,会从IOC容器中获取PrototypeCircularBeanA类型的Bean对象,并打印PrototypeCircularBeanA类型的Bean对象和依赖的PrototypeCircularBeanB类型的Bean对象。

(5)运行PrototypeCircularTest类

运行PrototypeCircularTest类的main()方法,输出的结果信息如下所示。

Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'prototypeCircularBeanA': Unsatisfied dependency expressed through field 'prototypeCircularBeanB': Error creating bean with name 'prototypeCircularBeanB': Unsatisfied dependency expressed through field 'prototypeCircularBeanA': Error creating bean with name 'prototypeCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?/***************省略其他输出信息**************/Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'prototypeCircularBeanB': Unsatisfied dependency expressed through field 'prototypeCircularBeanA': Error creating bean with name 'prototypeCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?/***************省略其他输出信息**************/Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'prototypeCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?/***************省略其他输出信息**************/

可以看到,输出结果中打印出了循环依赖的异常信息,说明Spring不支持多例Bean的setter方法循环依赖。

4.3 代理对象的setter循环依赖

Spring默认是不支持基于代理对象的setter方法的循环依赖,本节,就简单实现一个基于代理对象的setter方法的循环依赖案例。具体实现步骤如下所示。

(1)新增ProxyCircularBeanA类

ProxyCircularBeanA类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.proxy.bean.ProxyCircularBeanA。

@Componentpublic class ProxyCircularBeanA {@Autowiredprivate ProxyCircularBeanB proxyCircularBeanB;@Asyncpublic ProxyCircularBeanB getProxyCircularBeanB() {return proxyCircularBeanB;}}

可以看到,ProxyCircularBeanA类型的Bean对象会依赖ProxyCircularBeanB类型的Bean对象,并且在getProxyCircularBeanB()方法上标注了@Async注解,当调用getProxyCircularBeanB()方法时,会通过AOP自动生成代理对象。

(2)新增ProxyCircularBeanB类

ProxyCircularBeanB类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.proxy.bean.ProxyCircularBeanB。

@Componentpublic class ProxyCircularBeanB {@Autowiredprivate ProxyCircularBeanA proxyCircularBeanA;@Asyncpublic ProxyCircularBeanA getProxyCircularBeanA() {return proxyCircularBeanA;}}

可以看到,ProxyCircularBeanB类型的Bean对象会依赖ProxyCircularBeanA类型的Bean对象,并且在getProxyCircularBeanA()方法上标注了@Async注解,当调用getProxyCircularBeanA()方法时,会通过AOP自动生成代理对象。

(3)新增ProxyCircularConfig类

ProxyCircularConfig类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.proxy.config.ProxyCircularConfig。

@EnableAsync@Configuration@ComponentScan(value = {"io.binghe.spring.annotation.chapter20.proxy"})public class ProxyCircularConfig {}

可以看到,在ProxyCircularConfig类上标注了@Configuration注解,说明ProxyCircularConfig类是案例程序的配置类。并且在ProxyCircularConfig类上使用@ComponentScan注解指定了要扫描的包名。同时,使用@EnableAsync注解开启了异步调用。

(4)新增ProxyCircularTest类

ProxyCircularTest类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.proxy.ProxyCircularTest。

public class ProxyCircularTest {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProxyCircularConfig.class);ProxyCircularBeanA proxyCircularBeanA = context.getBean(ProxyCircularBeanA.class);System.out.println("proxyCircularBeanA===>>>" + proxyCircularBeanA);System.out.println("proxyCircularBeanB===>>>" + proxyCircularBeanA.getProxyCircularBeanB());context.close();}}

可以看到,在ProxyCircularTest类的main()方法中,创建完IOC容器后,会从IOC容器中获取ProxyCircularBeanA类型的Bean对象,并打印ProxyCircularBeanA类型的Bean对象和依赖的ProxyCircularBeanB类型的Bean对象。

(5)运行ProxyCircularTest类

运行ProxyCircularTest类的main()方法,输出的结果信息如下所示。

Exception in thread "main" org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'proxyCircularBeanA': Bean with name 'proxyCircularBeanA' has been injected into other beans [proxyCircularBeanB] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.

从输出的结果信息可以看出,Spring抛出了循环依赖的异常。说明Spring默认不支持基于代理对象的setter方法的循环依赖。

4.4 构造方法的循环依赖

Spring不支持基于构造方法的循环依赖。本节,就简单实现一个基于构造方法的循环依赖,具体实现步骤如下所示。

(1)新增ConstructCircularBeanA类

ConstructCircularBeanA类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.construct.bean.ConstructCircularBeanA。

@Componentpublic class ConstructCircularBeanA {private ConstructCircularBeanB constructCircularBeanB;private ConstructCircularBeanA(ConstructCircularBeanB constructCircularBeanB){this.constructCircularBeanB = constructCircularBeanB;}public ConstructCircularBeanB getConstructCircularBeanB() {return constructCircularBeanB;}}

可以看到,在ConstructCircularBeanA类中通过构造方法依赖了ConstructCircularBeanB类型的Bean对象,并提供了getConstructCircularBeanB()方法来获取ConstructCircularBeanB类型的Bean对象。

(2)新增ConstructCircularBeanB类

ConstructCircularBeanB类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.construct.bean.ConstructCircularBeanB。

@Componentpublic class ConstructCircularBeanB {private ConstructCircularBeanA constructCircularBeanA;public ConstructCircularBeanB(ConstructCircularBeanA constructCircularBeanA) {this.constructCircularBeanA = constructCircularBeanA;}public ConstructCircularBeanA getConstructCircularBeanA() {return constructCircularBeanA;}}

可以看到,在ConstructCircularBeanB类中通过构造方法依赖了ConstructCircularBeanA类型的Bean对象,并提供了getConstructCircularBeanA()方法来获取ConstructCircularBeanA类型的Bean对象。

(3)新增ConstructCircularConfig类

ConstructCircularConfig类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.construct.config.ConstructCircularConfig。

@Configuration@ComponentScan(value = {"io.binghe.spring.annotation.chapter20.construct"})public class ConstructCircularConfig {}

可以看到,在ConstructCircularConfig类上标注了@Configuration注解,说明ConstructCircularConfig类是案例程序的配置类,并且使用@ComponentScan注解指定了要扫描的包名。

(4)新增ConstructCircularTest类

ConstructCircularTest类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.construct.ConstructCircularTest。

public class ConstructCircularTest {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConstructCircularConfig.class);ConstructCircularBeanA constructCircularBeanA = context.getBean(ConstructCircularBeanA.class);System.out.println("cnotallow===>>>" + constructCircularBeanA);System.out.println("cnotallow===>>>" + constructCircularBeanA.getConstructCircularBeanB());context.close();}}

可以看到,在ConstructCircularTest类的main()方法中,创建完IOC容器后,会从IOC容器中获取ConstructCircularBeanA类型的Bean对象,并打印ConstructCircularBeanA类型的Bean对象和依赖的ConstructCircularBeanB类型的Bean对象。

(5)运行ConstructCircularTest类

运行ConstructCircularTest类的main()方法,输出的结果信息如下所示。

Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'constructCircularBeanA' defined in file [D:Workspaces2022springspring-annotation-bookspring-annotationspring-annotation-chapter-20targetclassesiobinghespringannotationchapter20constructbeanConstructCircularBeanA.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'constructCircularBeanB' defined in file [D:Workspaces2022springspring-annotation-bookspring-annotationspring-annotation-chapter-20targetclassesiobinghespringannotationchapter20constructbeanConstructCircularBeanB.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'constructCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?/*************省略其他信息**************/Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'constructCircularBeanB' defined in file [D:Workspaces2022springspring-annotation-bookspring-annotationspring-annotation-chapter-20targetclassesiobinghespringannotationchapter20constructbeanConstructCircularBeanB.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'constructCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?/*************省略其他信息**************/Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'constructCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?

可以看到,Spring抛出了循环依赖的异常,说明Spring不支持基于构造方法的循环依赖。

4.5 @DependsOn的循环依赖

@DependsOn注解主要用于指定Bean的实例化顺序,Spring默认是不支持基于@DependsOn注解的循环依赖。本节,就实现基于@DependsOn注解的循环依赖。具体实现步骤如下所示。

(1)新增DependsOnCircularBeanA类

DependsOnCircularBeanA类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.dependson.bean.DependsOnCircularBeanA。

@Component@DependsOn("dependsOnCircularBeanB")public class DependsOnCircularBeanA {@Autowiredprivate DependsOnCircularBeanB dependsOnCircularBeanB;public DependsOnCircularBeanB getDependsOnCircularBeanB() {return dependsOnCircularBeanB;}}

可以看到,在DependsOnCircularBeanA类上不仅标注了@Component注解,也标注了@DependsOn注解指定依赖dependsOnCircularBeanB,并且在DependsOnCircularBeanA类中使用@Autowired注解注入了DependsOnCircularBeanB类型的Bean对象。同时,在DependsOnCircularBeanA类中提供了getDependsOnCircularBeanB()方法获取DependsOnCircularBeanB类型的Bean对象。

(2)新增DependsOnCircularBeanB类

DependsOnCircularBeanB类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.dependson.bean.DependsOnCircularBeanB。

@Component@DependsOn("dependsOnCircularBeanA")public class DependsOnCircularBeanB {@Autowiredprivate DependsOnCircularBeanA dependsOnCircularBeanA;public DependsOnCircularBeanA getDependsOnCircularBeanA() {return dependsOnCircularBeanA;}}

可以看到,在DependsOnCircularBeanB类上不仅标注了@Component注解,也标注了@DependsOn注解指定依赖dependsOnCircularBeanA,并且在DependsOnCircularBeanB类中使用@Autowired注解注入了DependsOnCircularBeanA类型的Bean对象。同时,在DependsOnCircularBeanB类中提供了getDependsOnCircularBeanA()方法获取DependsOnCircularBeanA类型的Bean对象。

(3)新增DependsOnCircularConfig类

DependsOnCircularConfig类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.dependson.config.DependsOnCircularConfig。

@Configuration@ComponentScan(value = {"io.binghe.spring.annotation.chapter20.dependson"})public class DependsOnCircularConfig {}

可以看到,在DependsOnCircularConfig类上标注了@Configuration注解,说明DependsOnCircularConfig类是案例程序的配置类。同时,使用@ComponentScan注解指定了要扫描的包名。

(4)新增DependsOnCircularTest类

DependsOnCircularTest类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.dependson.DependsOnCircularTest。

public class DependsOnCircularTest {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DependsOnCircularConfig.class);DependsOnCircularBeanA dependsOnCircularBeanA = context.getBean(DependsOnCircularBeanA.class);System.out.println("dependsOnCircularBeanA===>>>" + dependsOnCircularBeanA);System.out.println("dependsOnCircularBeanB===>>>" + dependsOnCircularBeanA.getDependsOnCircularBeanB());context.close();}}

可以看到,在DependsOnCircularTest类的main()方法中,创建完IOC容器后,会从IOC容器中获取DependsOnCircularBeanA类型的Bean对象,并打印DependsOnCircularBeanA类型的Bean对象和依赖的DependsOnCircularBeanB类型的Bean对象。

(5)运行DependsOnCircularTest类

运行DependsOnCircularTest类的main()方法,输出的结果信息如下所示。

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dependsOnCircularBeanB' defined in file [D:Workspaces2022springspring-annotation-bookspring-annotationspring-annotation-chapter-20targetclassesiobinghespringannotationchapter20dependsonbeanDependsOnCircularBeanB.class]: Circular depends-on relationship between 'dependsOnCircularBeanB' and 'dependsOnCircularBeanA'

从输出的结果信息可以看出,Spring抛出了循环依赖的异常。说明Spring不支持@DependsOn注解的循环依赖。

4.6 单例Bean的setter循环依赖

Spring支持基于单例Bean的setter方法的循环依赖。本节,就实现基于单例Bean的setter方法的循环依赖案例。具体实现步骤如下所示。

(1)新增SingletonCircularBeanA类

SingletonCircularBeanA类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.singleton.bean.SingletonCircularBeanA。

@Componentpublic class SingletonCircularBeanA {@Autowiredprivate SingletonCircularBeanB singletonCircularBeanB;public SingletonCircularBeanB getSingletonCircularBeanB() {return singletonCircularBeanB;}}

可以看到,在SingletonCircularBeanA类中依赖了SingletonCircularBeanB类型的Bean对象,并提供了getSingletonCircularBeanB()方法获取SingletonCircularBeanB类型的Bean对象。

(2)新增SingletonCircularBeanB类

SingletonCircularBeanB类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.singleton.bean.SingletonCircularBeanB。

@Componentpublic class SingletonCircularBeanB {@Autowiredprivate SingletonCircularBeanA singletonCircularBeanA;public SingletonCircularBeanA getSingletonCircularBeanA() {return singletonCircularBeanA;}}

可以看到,在SingletonCircularBeanB类中依赖了SingletonCircularBeanA类型的Bean对象,并提供了getSingletonCircularBeanA()方法获取SingletonCircularBeanA类型的Bean对象。

(3)新增SingletonCircularConfig类

SingletonCircularConfig类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.singleton.config.SingletonCircularConfig。

@Configuration@ComponentScan(value = {"io.binghe.spring.annotation.chapter20.singleton"})public class SingletonCircularConfig {}

可以看到,在SingletonCircularConfig类上标注了@Configuration注解,说明SingletonCircularConfig类是案例程序的配置类,并使用@ComponentScan注解指定了要扫描的包名。

(4)新增SingletonCircularTest类

SingletonCircularTest类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.singleton.SingletonCircularTest。

public class SingletonCircularTest {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SingletonCircularConfig.class);SingletonCircularBeanA singletonCircularBeanA = context.getBean(SingletonCircularBeanA.class);System.out.println("singletnotallow===>>>" + singletonCircularBeanA);System.out.println("singletnotallow===>>>" + singletonCircularBeanA.getSingletonCircularBeanB());context.close();}}

可以看到,在SingletonCircularTest类的main()方法中,创建完IOC容器后,会从IOC容器中获取SingletonCircularBeanA类型的Bean对象,并打印SingletonCircularBeanA类型的Bean对象和依赖的SingletonCircularBeanB类型的Bean对象。

(5)运行SingletonCircularTest类

运行SingletonCircularTest类的main()方法,输出的结果信息如下所示。

singletnotallow===>>>io.binghe.spring.annotation.chapter20.singleton.bean.SingletonCircularBeanA@41e1e210singletnotallow===>>>io.binghe.spring.annotation.chapter20.singleton.bean.SingletonCircularBeanB@be35cd9

可以看到,正确输出了SingletonCircularBeanA类型的Bean对象和SingletonCircularBeanB类型的Bean对象。**说明Spring支持基于单例Bean的setter方法的循环依赖。

五、Spring循环依赖底层解决方案分析

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

由循环依赖场景的分析得知:Spring除了默认支持基于单例Bean的setter方法的循环依赖外,默认均不支持其他情况下的循环依赖。但是,如果是通过标注@Async注解生成的代理对象,则可以通过将标注了@Async注解的类排到后面加载的IOC容器中即可解决循环依赖的问题。

5.1 不支持单例Bean的setter循环依赖的特殊情况

运行4.1节中SpecialCircularTest类的main()方法,输出的结果信息如下所示。

Exception in thread "main" java.lang.StackOverflowError

可以看到,实际抛出的是StackOverflowError错误。这个错误信息本质上不是Spring抛出的,而是JVM抛出的。根本原因其实是出在SpecialCircularBeanA和SpecialCircularBeanB两个类的toString()方法上。

当在SpecialCircularTest类的main()方法中打印specialCircularBeanA时,默认会调用SpecialCircularBeanA类的toString()方法,在SpecialCircularBeanA类的toString()方法中,会拼接specialCircularBeanB对象,此时又会调用SpecialCircularBeanB类的toString()方法。而在SpecialCircularBeanB类的toString()方法中,又会拼接specialCircularBeanA对象,此时又会调用SpecialCircularBeanA类的toString()方法。在SpecialCircularBeanA类的toString()方法中,又会拼接specialCircularBeanB对象,继而调用SpecialCircularBeanB类的toString()方法....如此反复,造成了死循环。

简单点说,就是在SpecialCircularBeanA类的toString()方法中调用了SpecialCircularBeanB类的toString()方法,在SpecialCircularBeanB类的toString()方法中调用了SpecialCircularBeanA类的toString()方法,造成了死循环,最终抛出StackOverflowError错误。

5.2 不支持多例Bean的setter循环依赖

运行4.2节中PrototypeCircularTest类的main()方法,输出的结果信息如下所示。

Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'prototypeCircularBeanA': Unsatisfied dependency expressed through field 'prototypeCircularBeanB': Error creating bean with name 'prototypeCircularBeanB': Unsatisfied dependency expressed through field 'prototypeCircularBeanA': Error creating bean with name 'prototypeCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?/***************省略其他输出信息**************/Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'prototypeCircularBeanB': Unsatisfied dependency expressed through field 'prototypeCircularBeanA': Error creating bean with name 'prototypeCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?/***************省略其他输出信息**************/Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'prototypeCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?/***************省略其他输出信息**************/

Spring抛出了循环依赖的异常,说明Spring不支持多例Bean的setter循环依赖。接下来就分析下Spring中为啥不支持多例Bean的setter循环依赖。具体分析步骤如下所示。

(1)解析AbstractBeanFactory类的doGetBean(String name, @Nullable ClassrequiredType, @Nullable Object[] args, boolean typeCheckOnly)方法

源码详见:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean(String name, @Nullable ClassrequiredType, @Nullable Object[] args, boolean typeCheckOnly)。先来看在doGetBean()方法中创建多例Bean对象的逻辑,如下所示。

protected T doGetBean(String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {/**********省略其他代码*************/}else {/**********省略其他代码*************/try {/**********省略其他代码*************/else if (mbd.isPrototype()) {Object prototypeInstance = null;try {beforePrototypeCreation(beanName);prototypeInstance = createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);} /**********省略其他代码*************/}catch (BeansException ex) {/**********省略其他代码*************/}finally {/**********省略其他代码*************/}}return adaptBeanInstance(name, beanInstance, requiredType);}

可以看到,在多例Bean模式下,创建Bean对象之前会调用beforePrototypeCreation()方法,在创建Bean对象之后会调用afterPrototypeCreation()方法。

(2)解析AbstractBeanFactory类的beforePrototypeCreation(String beanName)方法

源码详见:org.springframework.beans.factory.support.AbstractBeanFactory#beforePrototypeCreation(String beanName)。

protected void beforePrototypeCreation(String beanName) {Object curVal = this.prototypesCurrentlyInCreation.get();if (curVal == null) {this.prototypesCurrentlyInCreation.set(beanName);}else if (curVal instanceof String strValue) {Set beanNameSet = new HashSet(2);beanNameSet.add(strValue);beanNameSet.add(beanName);this.prototypesCurrentlyInCreation.set(beanNameSet);}else {Set beanNameSet = (Set) curVal;beanNameSet.add(beanName);}}

可以看到,Spring在创建多例Bean时,会在beforePrototypeCreation()方法中,使用prototypesCurrentlyInCreation记录正在创建中的Bean,那prototypesCurrentlyInCreation又是个什么鬼呢?

prototypesCurrentlyInCreation的源码详见:org.springframework.beans.factory.support.AbstractBeanFactory#prototypesCurrentlyInCreation,如下所示。

private final ThreadLocal prototypesCurrentlyInCreation = new NamedThreadLocal("Prototype beans currently in creation");

也就是说,Spring在创建多例Bean时,会使用一个ThreadLocal类型的变量prototypesCurrentlyInCreation来记录当前线程正在创建中的Bean。并且根据beforePrototypeCreation()方法的源码又可以看出,在prototypesCurrentlyInCreation变量中使用一个Set集合来存储正在创建中的Bean。由于Set集合不存在重复对象,所以这样就能够保证在一个线程中只能有一个相同的Bean正在被创建。

(3)解析AbstractBeanFactory类的afterPrototypeCreation(String beanName)方法

源码详见:org.springframework.beans.factory.support.AbstractBeanFactory#afterPrototypeCreation(String beanName)。

protected void afterPrototypeCreation(String beanName) {Object curVal = this.prototypesCurrentlyInCreation.get();if (curVal instanceof String) {this.prototypesCurrentlyInCreation.remove();}else if (curVal instanceof Set beanNameSet) {beanNameSet.remove(beanName);if (beanNameSet.isEmpty()) {this.prototypesCurrentlyInCreation.remove();}}}

可以看到,afterPrototypeCreation()方法中主要是对prototypesCurrentlyInCreation中存储的Bean进行移除操作。

综合beforePrototypeCreation()方法和afterPrototypeCreation()方法可以看出,Spring在创建多例Bean之前,会将当前线程正在创建的Bean存入prototypesCurrentlyInCreation中,待Bean对象实例化完成后,就从prototypesCurrentlyInCreation中移除正在创建的Bean。

(4)返回AbstractBeanFactory类的doGetBean(String name, @Nullable ClassrequiredType, @Nullable Object[] args, boolean typeCheckOnly)方法。此时重点关注doGetBean()方法开始部分的代码片段。protected T doGetBean(String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException {String beanName = transformedBeanName(name);Object beanInstance;Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) { /**********省略其他代码*************/beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);}else {if (isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}/**********省略其他代码*************/}return adaptBeanInstance(name, beanInstance, requiredType);}

在AbstractBeanFactory类的doGetBean()方法开始的部分,会调用getSingleton()方法从缓存中获取单例Bean对象,首先会执行if条件进行判断,如果获取到的单例Bean对象为空,说明此时可能是第一次执行doGetBean()方法,也可能是创建的多例Bean。接下来会进入else分支逻辑,在else分支逻辑中,首先会判断当前线程是否已经存在正在创建的Bean,如果存在,则直接抛出BeanCurrentlyInCreationException异常。

(5)解析AbstractBeanFactory类的isPrototypeCurrentlyInCreation(String beanName)方法

源码详见:org.springframework.beans.factory.support.AbstractBeanFactory#isPrototypeCurrentlyInCreation(String beanName)。

protected boolean isPrototypeCurrentlyInCreation(String beanName) {Object curVal = this.prototypesCurrentlyInCreation.get();return (curVal != null && (curVal.equals(beanName) || (curVal instanceof Set set && set.contains(beanName))));}

所以,在Spring创建多例Bean时,无法解决Bean的循环依赖。如果在创建多例Bean的过程中,发现存在循环依赖,则直接抛出BeanCurrentlyInCreationException异常。

5.3 不支持代理对象的setter循环依赖

运行4.3节中ProxyCircularTest类的main()方法,输出的结果信息如下所示。

Exception in thread "main" org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'proxyCircularBeanA': Bean with name 'proxyCircularBeanA' has been injected into other beans [proxyCircularBeanB] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.

从输出的结果信息可以看出,Spring抛出了循环依赖的异常。接下来,具体分析Spring为何不支持代理对象的setter循环依赖。

(1)解析AbstractAutowireCapableBeanFactory类的doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)方法。

通过前面章节对于创建Bean的流程分析可知,无论是创建单例Bean还是创建多例Bean,Spring都会执行到AbstractAutowireCapableBeanFactory类的doCreateBean()方法中。

源码详见:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)。重点关注如下代码片段。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { /********省略其他代码**********/ boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));/********省略其他代码**********/if (earlySingletonExposure) {Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {if (exposedObject == bean) {exposedObject = earlySingletonReference;}else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {String[] dependentBeans = getDependentBeans(beanName);Set actualDependentBeans = new LinkedHashSet(dependentBeans.length);for (String dependentBean : dependentBeans) {if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}if (!actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " +"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");}}}} /********省略其他代码**********/return exposedObject;}

可以看到,在AbstractAutowireCapableBeanFactory类的doCreateBean()方法中,会判断从二级缓存中获取到的对象是否等于原始对象,代码片段如下所示。

Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {if (exposedObject == bean) {exposedObject = earlySingletonReference;}

此时,如果是代理对象的话,二级缓存中会存放由AOP生成出来的代理对象,与原始对象不相等。所以,会抛出BeanCurrentlyInCreationException异常。

另外,仔细观察AbstractAutowireCapableBeanFactory类的doCreateBean()方法时,会发现如果从二级缓存中获取到的earlySingletonReference对象为空,就会直接返回,不会抛出BeanCurrentlyInCreationException异常。由于Spring默认会按照文件全路径递归搜索,并且会按照路径+文件名的方式进行排序,排序靠前的Bean先被加载。所以,将标注了@Async注解的类排在后面即可解决循环依赖的问题。

5.4 不支持构造方法的循环依赖

运行4.4节中ConstructCircularTest类的main()方法,输出的结果信息如下所示。

Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'constructCircularBeanA' defined in file [D:Workspaces2022springspring-annotation-bookspring-annotationspring-annotation-chapter-20targetclassesiobinghespringannotationchapter20constructbeanConstructCircularBeanA.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'constructCircularBeanB' defined in file [D:Workspaces2022springspring-annotation-bookspring-annotationspring-annotation-chapter-20targetclassesiobinghespringannotationchapter20constructbeanConstructCircularBeanB.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'constructCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?/*************省略其他信息**************/Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'constructCircularBeanB' defined in file [D:Workspaces2022springspring-annotation-bookspring-annotationspring-annotation-chapter-20targetclassesiobinghespringannotationchapter20constructbeanConstructCircularBeanB.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'constructCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?/*************省略其他信息**************/Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'constructCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?

可以看到,Spring抛出了循环依赖的异常,说明Spring不支持基于构造方法的循环依赖。接下来,具体分析下Spring不支持基于构造方法的循环依赖的原因。具体分析步骤如下所示。

(1)解析AbstractBeanFactory类的doGetBean(String name, @Nullable ClassrequiredType, @Nullable Object[] args, boolean typeCheckOnly)方法

源码详见:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean(String name, @Nullable ClassrequiredType, @Nullable Object[] args, boolean typeCheckOnly)。

此时重点关注如下代码片段。

protected T doGetBean(String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {String beanName = transformedBeanName(name);Object beanInstance;Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {/********省略其他代码**********/} else {/********省略其他代码**********/if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {try {return createBean(beanName, mbd, args);}catch (BeansException ex) {/********省略其他代码**********/}});beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}/********省略其他代码**********/}catch (BeansException ex) {/********省略其他代码**********/}finally {/********省略其他代码**********/}}return adaptBeanInstance(name, beanInstance, requiredType);}

可以看到,在AbstractBeanFactory类的doGetBean()方法中,会调用getSingleton()方法。

(2)解析DefaultSingletonBeanRegistry类的getSingleton(String beanName, ObjectFactory singletonFactory)方法

源码详见:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(String beanName, ObjectFactory singletonFactory)。

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) {if (this.singletonsCurrentlyInDestruction) {throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)");}if (logger.isDebugEnabled()) {logger.debug("Creating shared instance of singleton bean '" + beanName + "'");}beforeSingletonCreation(beanName);boolean newSingleton = false;boolean recordSuppressedExceptions = (this.suppressedExceptions == null);if (recordSuppressedExceptions) {this.suppressedExceptions = new LinkedHashSet();}try {singletonObject = singletonFactory.getObject();newSingleton = true;}catch (IllegalStateException ex) {singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {throw ex;}}catch (BeanCreationException ex) {if (recordSuppressedExceptions) {for (Exception suppressedException : this.suppressedExceptions) {ex.addRelatedCause(suppressedException);}}throw ex;}finally {if (recordSuppressedExceptions) {this.suppressedExceptions = null;}afterSingletonCreation(beanName);}if (newSingleton) {addSingleton(beanName, singletonObject);}}return singletonObject;}}

可以看到,在getSingleton()方法创建Bean之前会调用beforeSingletonCreation()方法,在创建Bean之后会调用afterSingletonCreation()方法。

(3)解析DefaultSingletonBeanRegistry类的beforeSingletonCreation(String beanName)方法

源码详见:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#beforeSingletonCreation(String beanName)。

protected void beforeSingletonCreation(String beanName) {if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}}

可以看到,在创建单例Bean之前,会将要创建的Bean的名称添加到singletonsCurrentlyInCreation中,添加失败,说明singletonsCurrentlyInCreation集合中已经存在当前Bean的名称,发生了循环依赖,就会抛出BeanCurrentlyInCreationException异常。那么singletonsCurrentlyInCreation又是个什么鬼呢?

singletonsCurrentlyInCreation的源码详见:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#singletonsCurrentlyInCreation。

private final Set singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap(16));

可以看到,singletonsCurrentlyInCreation其实就是一个Set集合。也就是说,Spring会在创建单例Bean之前,将正在创建的Bean的名称添加到singletonsCurrentlyInCreation集合中。

(4)解析DefaultSingletonBeanRegistry类的afterSingletonCreation(String beanName)方法

源码详见:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#afterSingletonCreation(String beanName)。

protected void afterSingletonCreation(String beanName) {if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");}}

可以看到,在afterSingletonCreation()方法中,主要是移除singletonsCurrentlyInCreation中对应的Bean的名称。

综合beforeSingletonCreation()和afterSingletonCreation()两个方法可以看出,Spring在创建单例Bean之前,会将Bean的名称存入singletonsCurrentlyInCreation集合中,实例化Bean之后,会将Bean的名称从singletonsCurrentlyInCreation集合中移除。并且,Spring在创建单例Bean之前,会调用beforeSingletonCreation()方法将要创建的Bean的名称添加到singletonsCurrentlyInCreation中,添加失败,说明singletonsCurrentlyInCreation集合中已经存在当前Bean的名称,发生了循环依赖,就会抛出BeanCurrentlyInCreationException异常。

5.5 不支持@DependsOn的循环依赖

运行4.5节中DependsOnCircularTest类的main()方法,输出的结果信息如下所示。

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dependsOnCircularBeanB' defined in file [D:Workspaces2022springspring-annotation-bookspring-annotationspring-annotation-chapter-20targetclassesiobinghespringannotationchapter20dependsonbeanDependsOnCircularBeanB.class]: Circular depends-on relationship between 'dependsOnCircularBeanB' and 'dependsOnCircularBeanA'

从输出的结果信息可以看出,Spring抛出了循环依赖的异常。说明Spring不支持@DependsOn注解的循环依赖。接下来,就分析下Spring不支持@DependsOn注解的循环依赖的原因。具体分析步骤如下所示。

解析AbstractBeanFactory类的doGetBean(String name, @Nullable ClassrequiredType, @Nullable Object[] args, boolean typeCheckOnly)方法

源码详见:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean(String name, @Nullable ClassrequiredType, @Nullable Object[] args, boolean typeCheckOnly)。重点关注如下代码片段。

protected T doGetBean(String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {String beanName = transformedBeanName(name);Object beanInstance;Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) { /*********省略其他代码*********/}else {/*********省略其他代码*********/try {/*********省略其他代码*********/String[] dependsOn = mbd.getDependsOn();if (dependsOn != null) {for (String dep : dependsOn) {if (isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}registerDependentBean(dep, beanName);try {getBean(dep);}catch (NoSuchBeanDefinitionException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex);}}} /*********省略其他代码*********/}catch (BeansException ex) {/*********省略其他代码*********/}finally {/*********省略其他代码*********/}}return adaptBeanInstance(name, beanInstance, requiredType);}

可以看到,在AbstractBeanFactory类的doGetBean(String name, @Nullable ClassrequiredType, @Nullable Object[] args, boolean typeCheckOnly)方法中,会判断是否存在@DependsOn注解的循环依赖,如果存在则抛出BeanCreationException异常。所以,Spring不支持@DependsOn注解的循环依赖。

5.6 支持单例Bean的setter循环依赖

运行4.6节中SingletonCircularTest类的main()方法,输出的结果信息如下所示。

singletnotallow===>>>io.binghe.spring.annotation.chapter20.singleton.bean.SingletonCircularBeanA@41e1e210singletnotallow===>>>io.binghe.spring.annotation.chapter20.singleton.bean.SingletonCircularBeanB@be35cd9

可以看到,正确输出了SingletonCircularBeanA类型的Bean对象和SingletonCircularBeanB类型的Bean对象。说明Spring支持基于单例Bean的setter方法的循环依赖。接下来,就分析下Spring为何支持单例Bean的setter循环依赖。

(1)三级缓存

Spring使用了三级缓存来解决循环依赖的问题,三级缓存的源码详见:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry。

private final Map singletonObjects = new ConcurrentHashMap(256);private final Map singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}}}return singletonObject;}

通过上面的源码我们可以看到,在获取单例Bean的时候,会先从一级缓存singletonObjects里获取,如果没有获取到(说明不存在或没有实例化完成),会去第二级缓存earlySingletonObjects中去找,如果还是没有找到的话,就会三级缓存中获取单例工厂singletonFactory,通过从singletonFactory中获取正在创建中的引用,将singletonFactory存储在earlySingletonObjects 二级缓存中,这样就将创建中的单例引用从三级缓存中升级到了二级缓存中,二级缓存earlySingletonObjects,是会提前暴露已完成构造,还未执行属性注入的单例bean的。这个时候如何还有其他的bean也是需要属性注入,那么就可以直接从earlySingletonObjects中获取了。

注意:为了防止多线程并发环境下重复执行三级缓存中创建Bean的过程,在对singletonObjects加锁后,还会先从一级缓存singletonObjects中获取数据,如果数据不存在则从二级缓存earlySingletonObjects中获取数据,如果数据仍然不存在,才会从三级缓存singletonFactories中获取singletonFactory,调用singletonFactory的getObject()方法获取实例化但未对属性赋值的Bean对象,将其存入二级缓存,并且从三级缓存中移除对应的singletonFactory。

(4)解决循环依赖的完整流程图

最后给出Spring支持单例Bean的setter循环依赖的完整流程图,如图20-6所示。

万字长文带你彻底吃透Spring循环依赖

大家可以按照20-6的逻辑分析Spring解决循环依赖的代码,就相对比较清晰了。这里,就不再分析具体源码了。

六、总结

Spring的循环依赖问题介绍完了,我们一起总结下吧!

本章,主要详细分析了Spring的循环依赖问题,首先介绍了缓存依赖的基本概念和循环依赖的类型。随后以案例的形式详细介绍了循环依赖的场景,并详细分析了Spring循环依赖的底层解决方案。通过分析得知:Spring默认会支持单例Bean的setter循环依赖,对于其他情况下的循环依赖,Spring默认是不支持的。并且,最后给出了Spring解决循环依赖的流程图。

七、思考

既然学完了,就开始思考几个问题吧?

关于Spring的循环依赖,通常会有如下几个经典面试题:

什么是循环依赖问题?循环依赖有哪些类型?在Spring中支持哪种循环依赖?列举几种Spring不支持的循环依赖的场景,为什么不支持?Spring解决循环依赖的流程是什么?Spring解决缓存依赖时,二级缓存的作用是什么?Spring只用二级缓存能否解决循环依赖?为什么一定要用三级缓存来解决循环依赖呢?你从Spring解决循环依赖的设计中得到了哪些启发?

相关推荐: