万字长文带你彻底吃透Spring循环依赖 (万字长文带你从内存看指针)
一、学习指引
Spring中的循环依赖疑问,你真的彻底了解过吗?
Spring的循环依赖疑问可以说是面试环节中出现的十分频繁的疑问。比如,在面试环节中面试官或许会问:有了解过Spring中的循环依赖疑问吗?或许会问:什么是循环依赖疑问?或许会问:Spring中在哪些场景下会发生循环依赖疑问?或许会问:Spring是如何处置循环依赖疑问的?看似轻描淡写的一句话,实践要调查的内容也是比拟多的。面试官可以经过这个疑问调查面试者能否钻研过Spring的源码,有没有了解过Spring中的循环依赖疑问。
本章,咱们就一同来聊聊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所示。
3.1自我依赖
自我依赖就是自己依赖自己,从而构成的循环依赖,普通状况下不会出现这种循环依赖,如图20-2所示。
3.2直接依赖
直接依赖普通是出当初两个对象之间,例如对象A依赖对象B,对象B又依赖对象A,对象A和对象B之间构成了依赖相关,如图20-3所示。
3.3直接依赖
直接依赖普通是出当初三个或三个以上对象之间相互依赖的场景,例如对象A依赖对象B,对象B依赖对象C,对象C又依赖对象A,对象A、对象B和对象C之间就构成了循环依赖,如图20-4所示。
四、循环依赖场景
Spring中有哪些循环依赖的场景?
Spring中的循环依赖场景总体上可以分红单例Bean的setter循环依赖、多例Bean的setter循环依赖、代理对象的setter循环依赖、结构方法的循环依赖和DependsOn的循环依赖。如图20-5所示。
4.1单例Bean的setter循环依赖的不凡状况
Spring是支持基于单例Bean的setter方法的循环依赖的,不过有一种不凡状况须要留意。本节,咱们就一同成功基于单例Bean的setter方法的循环依赖的不凡状况,详细成功步骤如下所示。
(1)新增SpecialCircularBeanA类
SpecialCircularBeanA类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.special.bean.SpecialCircularBeanA。
@ComponentpublicclassSpecialCircularBeanA{@AutowiredprivateSpecialCircularBeanBspecialCircularBeanB;@OverridepublicStringtoString(){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。
@ComponentpublicclassSpecialCircularBeanB{@AutowiredprivateSpecialCircularBeanAspecialCircularBeanA;@OverridepublicStringtoString(){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"})publicclassSpecialCircularConfig{}
可以看到,在SpecialCircularConfig类上标注了@Configuration注解,说明SpecialCircularConfig类是案例程序的性能类,并且经常使用@ComponentScan注解指定了扫描的包。
(4)新增SpecialCircularTest类
SpecialCircularTest类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.special.SpecialCircularTest。
publicclassSpecialCircularTest{publicstaticvoidmn(String[]args){AnnotationConfiglicationContextcontext=newAnnotationConfigApplicationContext(SpecialCircularConfig.class);SpecialCircularBeanAspecialCircularBeanA=context.getBean(SpecialCircularBeanA.class);System.out.println(specialCircularBeanA);context.close();}}
可以看到,在SpecialCircularTest类的main()方法中,传入SpecialCircularConfig类的Class对象后,创立IOC容器,随后从IOC容器中失掉SpecialCircularBeanA类型的Bean对象并启动打印,最后封锁IOC容器。
(5)运转SpecialCircularTest类
运转SpecialCircularTest类的main()方法,输入的结果信息如下所示。
Exceptioninthread"main".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)publicclassPrototypeCircularBeanA{@AutowiredprivatePrototypeCircularBeanBprototypeCircularBeanB;publicPrototypeCircularBeanBgetPrototypeCircularBeanB(){returnprototypeCircularBeanB;}}
可以看到,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)publicclassPrototypeCircularBeanB{@AutowiredprivatePrototypeCircularBeanAprototypeCircularBeanA;publicPrototypeCircularBeanAgetPrototypeCircularBeanA(){returnprototypeCircularBeanA;}}
可以看到,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"})publicclassPrototypeCircularConfig{}
可以看到,在PrototypeCircularConfig类上标注了@Configuration注解,说明PrototypeCircularConfig类是案例程序的性能类,并且经常使用@ComponentScan注解指定了要扫描的包名。
(4)新增PrototypeCircularTest类
PrototypeCircularTest类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.prototype.PrototypeCircularTest。
publicclassPrototypeCircularTest{publicstaticvoidmain(String[]args){AnnotationConfigApplicationContextcontext=newAnnotationConfigApplicationContext(PrototypeCircularConfig.class);PrototypeCircularBeanAprototypeCircularBeanA=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()方法,输入的结果信息如下所示。
Exceptioninthread"main"org.springframework.beans.factory.UnsatisfiedDependencyException:Errorcreatingbeanwithname'prototypeCircularBeanA':Unsatisfieddependencyexpressedthroughfield'prototypeCircularBeanB':Errorcreatingbeanwithname'prototypeCircularBeanB':Unsatisfieddependencyexpressedthroughfield'prototypeCircularBeanA':Errorcreatingbeanwithname'prototypeCircularBeanA':Requestedbeaniscurrentlyincreation:Isthereanunresolvablecircularreference?/***************省略其余输入信息**************/Causedby:org.springframework.beans.factory.UnsatisfiedDependencyException:Errorcreatingbeanwithname'prototypeCircularBeanB':Unsatisfieddependencyexpressedthroughfield'prototypeCircularBeanA':Errorcreatingbeanwithname'prototypeCircularBeanA':Requestedbeaniscurrentlyincreation:Isthereanunresolvablecircularreference?/***************省略其余输入信息**************/Causedby:org.springframework.beans.factory.BeanCurrentlyInCreationException:Errorcreatingbeanwithname'prototypeCircularBeanA':Requestedbeaniscurrentlyincreation:Isthereanunresolvablecircularreference?/***************省略其余输入信息**************/
可以看到,输入结果中打印出了循环依赖的意外信息,说明Spring不支持多例Bean的setter方法循环依赖。
4.3代理对象的setter循环依赖
Spring自动是不支持基于代理对象的setter方法的循环依赖,本节,就繁难成功一个基于代理对象的setter方法的循环依赖案例。详细成功步骤如下所示。
(1)新增ProxyCircularBeanA类
ProxyCircularBeanA类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.proxy.bean.ProxyCircularBeanA。
@ComponentpublicclassProxyCircularBeanA{@AutowiredprivateProxyCircularBeanBproxyCircularBeanB;@AsyncpublicProxyCircularBeanBgetProxyCircularBeanB(){returnproxyCircularBeanB;}}
可以看到,ProxyCircularBeanA类型的Bean对象会依赖ProxyCircularBeanB类型的Bean对象,并且在getProxyCircularBeanB()方法上标注了@Async注解,当调用getProxyCircularBeanB()方法时,会经过AOP智能生成代理对象。
(2)新增ProxyCircularBeanB类
ProxyCircularBeanB类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.proxy.bean.ProxyCircularBeanB。
@ComponentpublicclassProxyCircularBeanB{@AutowiredprivateProxyCircularBeanAproxyCircularBeanA;@AsyncpublicProxyCircularBeanAgetProxyCircularBeanA(){returnproxyCircularBeanA;}}
可以看到,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"})publicclassProxyCircularConfig{}
可以看到,在ProxyCircularConfig类上标注了@Configuration注解,说明ProxyCircularConfig类是案例程序的性能类。并且在ProxyCircularConfig类上经常使用@ComponentScan注解指定了要扫描的包名。同时,经常使用@EnableAsync注解开启了异步伐用。
(4)新增ProxyCircularTest类
ProxyCircularTest类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.proxy.ProxyCircularTest。
publicclassProxyCircularTest{publicstaticvoidmain(String[]args){AnnotationConfigApplicationContextcontext=newAnnotationConfigApplicationContext(ProxyCircularConfig.class);ProxyCircularBeanAproxyCircularBeanA=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()方法,输入的结果信息如下所示。
Exceptioninthread"main"org.springframework.beans.factory.BeanCurrentlyInCreationException:Errorcreatingbeanwithname'proxyCircularBeanA':Beanwithname'proxyCircularBeanA'hasbeeninjectedintootherbeans[proxyCircularBeanB]initsrawversionaspartofacircularreference,buthaseventuallybeenwrapped.Thismeansthatsaidotherbeansdonotusethefinalversionofthebean.Thisisoftentheresultofover-eagertypematching-considerusing'getBeanNamesForType'withthe'allowEagerInit'flagturnedoff,forexample.
从输入的结果信息可以看出,Spring抛出了循环依赖的意外。说明Spring自动不支持基于代理对象的setter方法的循环依赖。
4.4结构方法的循环依赖
Spring不支持基于结构方法的循环依赖。本节,就繁难成功一个基于结构方法的循环依赖,详细成功步骤如下所示。
(1)新增ConstructCircularBeanA类
ConstructCircularBeanA类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.construct.bean.ConstructCircularBeanA。
@ComponentpublicclassConstructCircularBeanA{privateConstructCircularBeanBconstructCircularBeanB;privateConstructCircularBeanA(ConstructCircularBeanBconstructCircularBeanB){this.constructCircularBeanB=constructCircularBeanB;}publicConstructCircularBeanBgetConstructCircularBeanB(){returnconstructCircularBeanB;}}
可以看到,在ConstructCircularBeanA类中经过结构方法依赖了ConstructCircularBeanB类型的Bean对象,并提供了getConstructCircularBeanB()方法来失掉ConstructCircularBeanB类型的Bean对象。
(2)新增ConstructCircularBeanB类
ConstructCircularBeanB类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.construct.bean.ConstructCircularBeanB。
@ComponentpublicclassConstructCircularBeanB{privateConstructCircularBeanAconstructCircularBeanA;publicConstructCircularBeanB(ConstructCircularBeanAconstructCircularBeanA){this.constructCircularBeanA=constructCircularBeanA;}publicConstructCircularBeanAgetConstructCircularBeanA(){returnconstructCircularBeanA;}}
可以看到,在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"})publicclassConstructCircularConfig{}
可以看到,在ConstructCircularConfig类上标注了@Configuration注解,说明ConstructCircularConfig类是案例程序的性能类,并且经常使用@ComponentScan注解指定了要扫描的包名。
(4)新增ConstructCircularTest类
ConstructCircularTest类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.construct.ConstructCircularTest。
publicclassConstructCircularTest{publicstaticvoidmain(String[]args){AnnotationConfigApplicationContextcontext=newAnnotationConfigApplicationContext(ConstructCircularConfig.class);ConstructCircularBeanAconstructCircularBeanA=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()方法,输入的结果信息如下所示。
Exceptioninthread"main"org.springframework.beans.factory.UnsatisfiedDependencyException:Errorcreatingbeanwithname'constructCircularBeanA'definedinfile[D:Workspaces2022springspring-annotation-bookspring-annotationspring-annotation-chapter-20targetclassesiobinghespringannotationchapter20constructbeanConstructCircularBeanA.class]:Unsatisfieddependencyexpressedthroughconstructorparameter0:Errorcreatingbeanwithname'constructCircularBeanB'definedinfile[D:Workspaces2022springspring-annotation-bookspring-annotationspring-annotation-chapter-20targetclassesiobinghespringannotationchapter20constructbeanConstructCircularBeanB.class]:Unsatisfieddependencyexpressedthroughconstructorparameter0:Errorcreatingbeanwithname'constructCircularBeanA':Requestedbeaniscurrentlyincreation:Isthereanunresolvablecircularreference?/*************省略其余信息**************/Causedby:org.springframework.beans.factory.UnsatisfiedDependencyException:Errorcreatingbeanwithname'constructCircularBeanB'definedinfile[D:Workspaces2022springspring-annotation-bookspring-annotationspring-annotation-chapter-20targetclassesiobinghespringannotationchapter20constructbeanConstructCircularBeanB.class]:Unsatisfieddependencyexpressedthroughconstructorparameter0:Errorcreatingbeanwithname'constructCircularBeanA':Requestedbeaniscurrentlyincreation:Isthereanunresolvablecircularreference?/*************省略其余信息**************/Causedby:org.springframework.beans.factory.BeanCurrentlyInCreationException:Errorcreatingbeanwithname'constructCircularBeanA':Requestedbeaniscurrentlyincreation:Isthereanunresolvablecircularreference?
可以看到,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")publicclassDependsOnCircularBeanA{@AutowiredprivateDependsOnCircularBeanBdependsOnCircularBeanB;publicDependsOnCircularBeanBgetDependsOnCircularBeanB(){returndependsOnCircularBeanB;}}
可以看到,在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")publicclassDependsOnCircularBeanB{@AutowiredprivateDependsOnCircularBeanAdependsOnCircularBeanA;publicDependsOnCircularBeanAgetDependsOnCircularBeanA(){returndependsOnCircularBeanA;}}
可以看到,在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"})publicclassDependsOnCircularConfig{}
可以看到,在DependsOnCircularConfig类上标注了@Configuration注解,说明DependsOnCircularConfig类是案例程序的性能类。同时,经常使用@ComponentScan注解指定了要扫描的包名。
(4)新增DependsOnCircularTest类
DependsOnCircularTest类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.dependson.DependsOnCircularTest。
publicclassDependsOnCircularTest{publicstaticvoidmain(String[]args){AnnotationConfigApplicationContextcontext=newAnnotationConfigApplicationContext(DependsOnCircularConfig.class);DependsOnCircularBeanAdependsOnCircularBeanA=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()方法,输入的结果信息如下所示。
Exceptioninthread"main"org.springframework.beans.factory.BeanCreationException:Errorcreatingbeanwithname'dependsOnCircularBeanB'definedinfile[D:Workspaces2022springspring-annotation-bookspring-annotationspring-annotation-chapter-20targetclassesiobinghespringannotationchapter20dependsonbeanDependsOnCircularBeanB.class]:Circulardepends-onrelationshipbetween'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。
@ComponentpublicclassSingletonCircularBeanA{@AutowiredprivateSingletonCircularBeanBsingletonCircularBeanB;publicSingletonCircularBeanBgetSingletonCircularBeanB(){returnsingletonCircularBeanB;}}
可以看到,在SingletonCircularBeanA类中依赖了SingletonCircularBeanB类型的Bean对象,并提供了getSingletonCircularBeanB()方法失掉SingletonCircularBeanB类型的Bean对象。
(2)新增SingletonCircularBeanB类
SingletonCircularBeanB类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.singleton.bean.SingletonCircularBeanB。
@ComponentpublicclassSingletonCircularBeanB{@AutowiredprivateSingletonCircularBeanAsingletonCircularBeanA;publicSingletonCircularBeanAgetSingletonCircularBeanA(){returnsingletonCircularBeanA;}}
可以看到,在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"})publicclassSingletonCircularConfig{}
可以看到,在SingletonCircularConfig类上标注了@Configuration注解,说明SingletonCircularConfig类是案例程序的性能类,并经常使用@ComponentScan注解指定了要扫描的包名。
(4)新增SingletonCircularTest类
SingletonCircularTest类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.singleton.SingletonCircularTest。
publicclassSingletonCircularTest{publicstaticvoidmain(String[]args){AnnotationConfigApplicationContextcontext=newAnnotationConfigApplicationContext(SingletonCircularConfig.class);SingletonCircularBeanAsingletonCircularBeanA=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()方法,输入的结果信息如下所示。
Exceptioninthread"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()方法,输入的结果信息如下所示。
Exceptioninthread"main"org.springframework.beans.factory.UnsatisfiedDependencyException:Errorcreatingbeanwithname'prototypeCircularBeanA':Unsatisfieddependencyexpressedthroughfield'prototypeCircularBeanB':Errorcreatingbeanwithname'prototypeCircularBeanB':Unsatisfieddependencyexpressedthroughfield'prototypeCircularBeanA':Errorcreatingbeanwithname'prototypeCircularBeanA':Requestedbeaniscurrentlyincreation:Isthereanunresolvablecircularreference?/***************省略其余输入信息**************/Causedby:org.springframework.beans.factory.UnsatisfiedDependencyException:Errorcreatingbeanwithname'prototypeCircularBeanB':Unsatisfieddependencyexpressedthroughfield'prototypeCircularBeanA':Errorcreatingbeanwithname'prototypeCircularBeanA':Requestedbeaniscurrentlyincreation:Isthereanunresolvablecircularreference?/***************省略其余输入信息**************/Causedby:org.springframework.beans.factory.BeanCurrentlyInCreationException:Errorcreatingbeanwithname'prototypeCircularBeanA':Requestedbeaniscurrentlyincreation:Isthereanunresolvablecircularreference?/***************省略其余输入信息**************/
Spring抛出了循环依赖的意外,说明Spring不支持多例Bean的setter循环依赖。接上去就剖析下Spring中为啥不支持多例Bean的setter循环依赖。详细剖析步骤如下所示。
(1)解析AbstractBeanFactory类的doGetBean(Stringname,@NullableClassrequiredType,@NullableObject[]args,booleantypeCheckOnly)方法
源码详见:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean(Stringname,@NullableClassrequiredType,@NullableObject[]args,booleantypeCheckOnly)。先来看在doGetBean()方法中创立多例Bean对象的逻辑,如下所示。
protected<T>TdoGetBean(Stringname,@NullableClass<T>requiredType,@NullableObject[]args,booleantypeCheckOnly)throwsBeansException{ObjectsharedInstance=getSingleton(beanName);if(sharedInstance!=null&&args==null){/**********省略其余代码*************/}else{/**********省略其余代码*************/try{/**********省略其余代码*************/elseif(mbd.isPrototype()){ObjectprototypeInstance=null;try{beforePrototypeCreation(beanName);prototypeInstance=createBean(beanName,mbd,args);}finally{afterPrototypeCreation(beanName);}beanInstance=getObjectForBeanInstance(prototypeInstance,name,beanName,mbd);}/**********省略其余代码*************/}catch(BeansExceptionex){/**********省略其余代码*************/}finally{/**********省略其余代码*************/}}returnadaptBeanInstance(name,beanInstance,requiredType);}
可以看到,在多例Bean形式下,创立Bean对象之前会调用beforePrototypeCreation()方法,在创立Bean对象之后会调用afterPrototypeCreation()方法。
(2)解析AbstractBeanFactory类的beforePrototypeCreation(StringbeanName)方法
源码详见:org.springframework.beans.factory.support.AbstractBeanFactory#beforePrototypeCreation(StringbeanName)。
protectedvoidbeforePrototypeCreation(StringbeanName){ObjectcurVal=this.prototypesCurrentlyInCreation.get();if(curVal==null){this.prototypesCurrentlyInCreation.set(beanName);}elseif(curValinstanceofStringstrValue){Set<String>beanNameSet=newHashSet<>(2);beanNameSet.add(strValue);beanNameSet.add(beanName);this.prototypesCurrentlyInCreation.set(beanNameSet);}else{Set<String>beanNameSet=(Set<String>)curVal;beanNameSet.add(beanName);}}
可以看到,Spring在创立多例Bean时,会在beforePrototypeCreation()方法中,经常使用prototypesCurrentlyInCreation记载正在创立中的Bean,那prototypesCurrentlyInCreation又是个什么鬼呢?
prototypesCurrentlyInCreation的源码详见:org.springframework.beans.factory.support.AbstractBeanFactory#prototypesCurrentlyInCreation,如下所示。
privatefinalThreadLocal<Object>prototypesCurrentlyInCreation=newNamedThreadLocal<>("Prototypebeanscurrentlyincreation");
也就是说,Spring在创立多例Bean时,会经常使用一个ThreadLocal类型的变量prototypesCurrentlyInCreation来记载以后线程正在创立中的Bean。并且依据beforePrototypeCreation()方法的源码又可以看出,在prototypesCurrentlyInCreation变量中经常使用一个Set汇合来存储正在创立中的Bean。因为Set汇合不存在重复对象,所以这样就能够保证在一个线程中只能有一个相反的Bean正在被创立。
(3)解析AbstractBeanFactory类的afterPrototypeCreation(StringbeanName)方法
源码详见:org.springframework.beans.factory.support.AbstractBeanFactory#afterPrototypeCreation(StringbeanName)。
protectedvoidafterPrototypeCreation(StringbeanName){ObjectcurVal=this.prototypesCurrentlyInCreation.get();if(curValinstanceofString){this.prototypesCurrentlyInCreation.remove();}elseif(curValinstanceofSet<?>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(Stringname,@NullableClassrequiredType,@NullableObject[]args,booleantypeCheckOnly)方法。此时重点关注doGetBean()方法开局局部的代码片段。
protected<T>TdoGetBean(Stringname,@NullableClass<T>requiredType,@NullableObject[]args,booleantypeCheckOnly)throwsBeansException{StringbeanName=transformedBeanName(name);ObjectbeanInstance;ObjectsharedInstance=getSingleton(beanName);if(sharedInstance!=null&&args==null){/**********省略其余代码*************/beanInstance=getObjectForBeanInstance(sharedInstance,name,beanName,null);}else{if(isPrototypeCurrentlyInCreation(beanName)){thrownewBeanCurrentlyInCreationException(beanName);}/**********省略其余代码*************/}returnadaptBeanInstance(name,beanInstance,requiredType);}
在AbstractBeanFactory类的doGetBean()方法开局的局部,会调用getSingleton()方法从缓存中失掉单例Bean对象,首先会口头if条件启动判别,假设失掉到的单例Bean对象为空,说明此时或许是第一次性口头doGetBean()方法,也或许是创立的多例Bean。接上去会进入else分支逻辑,在else分支逻辑中,首先会判别以后线程能否曾经存在正在创立的Bean,假设存在,则直接抛出BeanCurrentlyInCreationException意外。
(5)解析AbstractBeanFactory类的isPrototypeCurrentlyInCreation(StringbeanName)方法
源码详见:org.springframework.beans.factory.support.AbstractBeanFactory#isPrototypeCurrentlyInCreation(StringbeanName)。
protectedbooleanisPrototypeCurrentlyInCreation(StringbeanName){ObjectcurVal=this.prototypesCurrentlyInCreation.get();return(curVal!=null&&(curVal.equals(beanName)||(curValinstanceofSet<?>set&&set.contains(beanName))));}
所以,在Spring创立多例Bean时,不可处置Bean的循环依赖。假设在创立多例Bean的环节中,发现存在循环依赖,则直接抛出BeanCurrentlyInCreationException意外。
5.3不支持代理对象的setter循环依赖
运转4.3节中ProxyCircularTest类的main()方法,输入的结果信息如下所示。
Exceptioninthread"main"org.springframework.beans.factory.BeanCurrentlyInCreationException:Errorcreatingbeanwithname'proxyCircularBeanA':Beanwithname'proxyCircularBeanA'hasbeeninjectedintootherbeans[proxyCircularBeanB]initsrawversionaspartofacircularreference,buthaseventuallybeenwrapped.Thismeansthatsaidotherbeansdonotusethefinalversionofthebean.Thisisoftentheresultofover-eagertypematching-considerusing'getBeanNamesForType'withthe'allowEagerInit'flagturnedoff,forexample.
从输入的结果信息可以看出,Spring抛出了循环依赖的意外。接上去,详细剖析Spring为何不支持代理对象的setter循环依赖。
(1)解析AbstractAutowireCapableBeanFactory类的doCreateBean(StringbeanName,RootBeanDefinitionmbd,@NullableObject[]args)方法。
经过前面章节关于创立Bean的流程剖析可知,无论是创立单例Bean还是创立多例Bean,Spring都会口头到AbstractAutowireCapableBeanFactory类的doCreateBean()方法中。
源码详见:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean(StringbeanName,RootBeanDefinitionmbd,@NullableObject[]args)。重点关注如下代码片段。
protectedObjectdoCreateBean(StringbeanName,RootBeanDefinitionmbd,@NullableObject[]args)throwsBeanCreationException{/********省略其余代码**********/booleanearlySingletonExposure=(mbd.isSingleton()&&this.allowCircularReferences&&isSingletonCurrentlyInCreation(beanName));/********省略其余代码**********/if(earlySingletonExposure){ObjectearlySingletonReference=getSingleton(beanName,false);if(earlySingletonReference!=null){if(exposedObject==bean){exposedObject=earlySingletonReference;}elseif(!this.allowRawInjectionDespiteWrapping&&hasDependentBean(beanName)){String[]dependentBeans=getDependentBeans(beanName);Set<String>actualDependentBeans=newLinkedHashSet<>(dependentBeans.length);for(StringdependentBean:dependentBeans){if(!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)){actualDependentBeans.add(dependentBean);}}if(!actualDependentBeans.isEmpty()){thrownewBeanCurrentlyInCreationException(beanName,"Beanwithname'"+beanName+"'hasbeeninjectedintootherbeans["+StringUtils.collectionToCommaDelimitedString(actualDependentBeans)+reference,buthaseventuallybeen"+"wrapped.Thismeansthatsaidotherbeansdonotusethefinalversionofthe"+"bean.Thisisoftentheresultofover-eagertypematching-considerusing"+"'getBeanNamesForType'withthe'allowEagerInit'flagturnedoff,forexample.");}}}}/********省略其余代码**********/returnexposedObject;}
可以看到,在AbstractAutowireCapableBeanFactory类的doCreateBean()方法中,会判别从二级缓存中失掉到的对象能否等于原始对象,代码片段如下所示。
ObjectearlySingletonReference=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()方法,输入的结果信息如下所示。
Exceptioninthread"main"org.springframework.beans.factory.UnsatisfiedDependencyException:Errorcreatingbeanwithname'constructCircularBeanA'definedinfile[D:Workspaces2022springspring-annotation-bookspring-annotationspring-annotation-chapter-20targetclassesiobinghespringannotationchapter20constructbeanConstructCircularBeanA.class]:Unsatisfieddependencyexpressedthroughconstructorparameter0:Errorcreatingbeanwithname'constructCircularBeanB'definedinfile[D:Workspaces2022springspring-annotation-bookspring-annotationspring-annotation-chapter-20targetclassesiobinghespringannotationchapter20constructbeanConstructCircularBeanB.class]:Unsatisfieddependencyexpressedthroughconstructorparameter0:Errorcreatingbeanwithname'constructCircularBeanA':Requestedbeaniscurrentlyincreation:Isthereanunresolvablecircularreference?/*************省略其余信息**************/Causedby:org.springframework.beans.factory.UnsatisfiedDependencyException:Errorcreatingbeanwithname'constructCircularBeanB'definedinfile[D:Workspaces2022springspring-annotation-bookspring-annotationspring-annotation-chapter-20targetclassesiobinghespringannotationchapter20constructbeanConstructCircularBeanB.class]:Unsatisfieddependencyexpressedthroughconstructorparameter0:Errorcreatingbeanwithname'constructCircularBeanA':Requestedbeaniscurrentlyincreation:Isthereanunresolvablecircularreference?/*************省略其余信息**************/Causedby:org.springframework.beans.factory.BeanCurrentlyInCreationException:Errorcreatingbeanwithname'constructCircularBeanA':Requestedbeaniscurrentlyincreation:Isthereanunresolvablecircularreference?
可以看到,Spring抛出了循环依赖的意外,说明Spring不支持基于结构方法的循环依赖。接上去,详细剖析下Spring不支持基于结构方法的循环依赖的要素。详细剖析步骤如下所示。
(1)解析AbstractBeanFactory类的doGetBean(Stringname,@NullableClassrequiredType,@NullableObject[]args,booleantypeCheckOnly)方法
源码详见:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean(Stringname,@NullableClassrequiredType,@NullableObject[]args,booleantypeCheckOnly)。
此时重点关注如下代码片段。
protected<T>TdoGetBean(Stringname,@NullableClass<T>requiredType,@NullableObject[]args,booleantypeCheckOnly)throwsBeansException{StringbeanName=transformedBeanName(name);ObjectbeanInstance;ObjectsharedInstance=getSingleton(beanName);if(sharedInstance!=null&&args==null){/********省略其余代码**********/}else{/********省略其余代码**********/if(mbd.isSingleton()){sharedInstance=getSingleton(beanName,()->{try{returncreateBean(beanName,mbd,args);}catch(BeansExceptionex){/********省略其余代码**********/}});beanInstance=getObjectForBeanInstance(sharedInstance,name,beanName,mbd);}/********省略其余代码**********/}catch(BeansExceptionex){/********省略其余代码**********/}finally{/********省略其余代码**********/}}returnadaptBeanInstance(name,beanInstance,requiredType);}
可以看到,在AbstractBeanFactory类的doGetBean()方法中,会调用getSingleton()方法。
(2)解析DefaultSingletonBeanRegistry类的getSingleton(StringbeanName,ObjectFactory<?>singletonFactory)方法
源码详见:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(StringbeanName,ObjectFactory<?>singletonFactory)。
publicObjectgetSingleton(StringbeanName,ObjectFactory<?>singletonFactory){Assert.notNull(beanName,"Beannamemustnotbenull");synchronized(this.singletonObjects){ObjectsingletonObject=this.singletonObjects.get(beanName);if(singletonObject==null){if(this.singletonsCurrentlyInDestruction){thrownewBeanCreationNotAllowedException(beanName,"Singletonbeancreationnotallowedwhilesingletonsofthisfactoryareindestruction"+"(DonotrequestabeanfromaBeanFactoryinadestroymethodimplementation!)");}if(logger.isDebugEnabled()){logger.debug("Creatingsharedinstanceofsingletonbean'"+beanName+"'");}beforeSingletonCreation(beanName);booleannewSingleton=false;booleanrecordSuppressedExceptions=(this.suppressedExceptions==null);if(recordSuppressedExceptions){this.suppressedExceptions=newLinkedHashSet<>();}try{singletonObject=singletonFactory.getObject();newSingleton=true;}catch(IllegalStateExceptionex){singletonObject=this.singletonObjects.get(beanName);if(singletonObject==null){throwex;}}catch(BeanCreationExceptionex){if(recordSuppressedExceptions){for(ExceptionsuppressedException:this.suppressedExceptions){ex.addRelatedCause(suppressedException);}}throwex;}finally{if(recordSuppressedExceptions){this.suppressedExceptions=null;}afterSingletonCreation(beanName);}if(newSingleton){addSingleton(beanName,singletonObject);}}returnsingletonObject;}}
可以看到,在getSingleton()方法创立Bean之前会调用beforeSingletonCreation()方法,在创立Bean之后会调用afterSingletonCreation()方法。
(3)解析DefaultSingletonBeanRegistry类的beforeSingletonCreation(StringbeanName)方法
源码详见:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#beforeSingletonCreation(StringbeanName)。
protectedvoidbeforeSingletonCreation(StringbeanName){if(!this.inCreationCheckExclusions.contains(beanName)&&!this.singletonsCurrentlyInCreation.add(beanName)){thrownewBeanCurrentlyInCreationException(beanName);}}
可以看到,在创立单例Bean之前,会将要创立的Bean的称号参与到singletonsCurrentlyInCreation中,参与失败,说明singletonsCurrentlyInCreation汇合中曾经存在以后Bean的称号,出现了循环依赖,就会抛出BeanCurrentlyInCreationException意外。那么singletonsCurrentlyInCreation又是个什么鬼呢?
singletonsCurrentlyInCreation的源码详见:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#singletonsCurrentlyInCreation。
privatefinalSet<String>singletonsCurrentlyInCreation=Collections.newSetFromMap(newConcurrentHashMap<>(16));
可以看到,singletonsCurrentlyInCreation其实就是一个Set汇合。也就是说,Spring会在创立单例Bean之前,将正在创立的Bean的称号参与到singletonsCurrentlyInCreation汇合中。
(4)解析DefaultSingletonBeanRegistry类的afterSingletonCreation(StringbeanName)方法
源码详见:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#afterSingletonCreation(StringbeanName)。
protectedvoidafterSingletonCreation(StringbeanName){if(!this.inCreationCheckExclusions.contains(beanName)&&!this.singletonsCurrentlyInCreation.remove(beanName)){thrownewIllegalStateException("Singleton'"+beanName+"'isn'tcurrentlyincreation");}}
可以看到,在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()方法,输入的结果信息如下所示。
Exceptioninthread"main"org.springframework.beans.factory.BeanCreationException:Errorcreatingbeanwithname'dependsOnCircularBeanB'definedinfile[D:Workspaces2022springspring-annotation-bookspring-annotationspring-annotation-chapter-20targetclassesiobinghespringannotationchapter20dependsonbeanDependsOnCircularBeanB.class]:Circulardepends-onrelationshipbetween'dependsOnCircularBeanB'and'dependsOnCircularBeanA'
从输入的结果信息可以看出,Spring抛出了循环依赖的意外。说明Spring不支持@DependsOn注解的循环依赖。接上去,就剖析下Spring不支持@DependsOn注解的循环依赖的要素。详细剖析步骤如下所示。
解析AbstractBeanFactory类的doGetBean(Stringname,@NullableClassrequiredType,@NullableObject[]args,booleantypeCheckOnly)方法
源码详见:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean(Stringname,@NullableClassrequiredType,@NullableObject[]args,booleantypeCheckOnly)。重点关注如下代码片段。
protected<T>TdoGetBean(Stringname,@NullableClass<T>requiredType,@NullableObject[]args,booleantypeCheckOnly)throwsBeansException{StringbeanName=transformedBeanName(name);ObjectbeanInstance;ObjectsharedInstance=getSingleton(beanName);if(sharedInstance!=null&&args==null){/*********省略其余代码*********/}else{/*********省略其余代码*********/try{/*********省略其余代码*********/String[]dependsOn=mbd.getDependsOn();if(dependsOn!=null){for(Stringdep:dependsOn){if(isDependent(beanName,dep)){thrownewBeanCreationException(mbd.getResourceDescription(),beanName,"Circulardepends-onrelationshipbetween'"+beanName+"'and'"+dep+"'");}registerDependentBean(dep,beanName);try{getBean(dep);}catch(NoSuchBeanDefinitionExceptionex){thrownewBeanCreationException(mbd.getResourceDescription(),beanName,"'"+beanName+"'dependsonmissingbean'"+dep+"'",ex);}}}/*********省略其余代码*********/}catch(BeansExceptionex){/*********省略其余代码*********/}finally{/*********省略其余代码*********/}}returnadaptBeanInstance(name,beanInstance,requiredType);}
可以看到,在AbstractBeanFactory类的doGetBean(Stringname,@NullableClassrequiredType,@NullableObject[]args,booleantypeCheckOnly)方法中,会判别能否存在@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。
privatefinalMap<String,Object>singletonObjects=newConcurrentHashMap<>(256);privatefinalMap<String,ObjectFactory<?>>singletonFactories=newHashMap<>(16);privatefinalMap<String,Object>earlySingletonObjects=newConcurrentHashMap<>(16);
其中,每个Map的含意如下所示。
关于三级缓存,面试中经常会问如下两个疑问,这里也给大家分享下。
二级缓存关键是为了分别创立出的完整的Bean和未对属性赋值的Bean,二级缓存中实践关键存储的是未对属性赋值的Bean,这样做的目标就是为了防止在多线程并发的场景中,读取到还未创立成功的Bean。所以,为了保证在多线程并发的环境中,读取到的Bean是完整的(曾经为属性赋值),不会读取到未对属性赋值的Bean,须要经常使用二级缓存处置循环依赖。
另外,就一、二、三级缓存而言,二级缓存关键存储的是三级缓存创立进去的,并且未对属性赋值的Bean,这样做的目标也是为了防止三级缓存中的工厂类重复口头创立对象的逻辑。
其实,Spring经常使用二级缓存就齐全能够处置循环依赖的疑问,也可以支持Spring基于BeanPostProcessor的裁减才干。然而,因为Spring中的方法在设计上遵照了繁多职责的准绳,一个方法通常只做一件事情,getBean()方法就是失掉Bean对象。然而,调用BeanPostProcessor创立灵活代理是处于创立Bean的环节,假设在getBean()中成功这个逻辑,显然代码逻辑比拟耦合。为了处置代码耦合的疑问,坚持方法的职责繁多,方背地期保养。须要将创立灵活代理的BeanPostProcessor放在创立Bean的方法中。并且将判别能否存在循环依赖的逻辑放在getSingleton()方法中。此时就须要三级缓存,在三级缓存中寄存一个工厂接口,在接口的成功类中调用BeanPostProcessor创立灵活代理对象。为了防止重复创立代理对象,将三级缓存中创立的代理对象存入二级缓存。在Spring中经常使用三级缓存完美处置了解耦、性能、裁减的疑问。
(2)创立单例工厂
Spring在创立Bean对象时,会先创立一个和Bean的称号相反的单例工厂,并将Bean先放入单例工厂中。
源码详见:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean(StringbeanName,RootBeanDefinitionmbd,@NullableObject[]args)。
protectedObjectdoCreateBean(StringbeanName,RootBeanDefinitionmbd,@NullableObject[]args)throwsBeanCreationException{/**********省略其余代码***********/if(earlySingletonExposure){/**********省略其余代码***********/addSingletonFactory(beanName,()->getEarlyBeanReference(beanName,mbd,bean));}/**********省略其余代码***********/returnexposedObject;}
在AbstractAutowireCapableBeanFactory类的doCreateBean()方法中调用了addSingletonFactory()方法。
addSingletonFactory()方法的源码详见:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory(StringbeanName,ObjectFactory<?>singletonFactory)。
protectedvoidaddSingletonFactory(StringbeanName,ObjectFactory<?>singletonFactory){Assert.notNull(singletonFactory,"Singletonfactorymustnotbenull");synchronized(this.singletonObjects){if(!this.singletonObjects.containsKey(beanName)){this.singletonFactories.put(beanName,singletonFactory);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}}}
可以看到,addSingletonFactory()方法的作用是将正在创立中的Bean的单例工厂,寄存在三级缓存里,这样就保证了在循环依赖查找的时刻是可以找到Bean的援用的。
(3)读取缓存数据
详细读取缓存失掉Bean的环节在类DefaultSingletonBeanRegistry的getSingleton()方法中,源码详见:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(StringbeanName,booleanallowEarlyReference)。
@NullableprotectedObjectgetSingleton(StringbeanName,booleanallowEarlyReference){//QuickcheckforexistinginstancewithoutfullsingletonlockObjectsingletonObject=this.singletonObjects.get(beanName);if(singletonObject==null&&isSingletonCurrentlyInCreation(beanName)){singletonObject=this.earlySingletonObjects.get(beanName);if(singletonObject==null&&allowEarlyReference){synchronized(this.singletonObjects){//ConsistentcreationofearlyreferencewithinfullsingletonlocksingletonObject=this.singletonObjects.get(beanName);if(singletonObject==null){singletonObject=this.earlySingletonObjects.get(beanName);if(singletonObject==null){ObjectFactory<?>singletonFactory=this.singletonFactories.get(beanName);if(singletonFactory!=null){singletonObject=singletonFactory.getObject();this.earlySingletonObjects.put(beanName,singletonObject);this.singletonFactories.remove(beanName);}}}}}}returnsingletonObject;}
经过上方的源码咱们可以看到,在失掉单例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所示。
大家可以依照20-6的逻辑剖析Spring处置循环依赖的代码,就相对比拟明晰了。这里,就不再剖析详细源码了。
六、总结
Spring的循环依赖疑问引见完了,咱们一同总结下吧!
本章,关键详细剖析了Spring的循环依赖疑问,首先引见了缓存依赖的基本概念和循环依赖的类型。随后以案例的方式详细引见了循环依赖的场景,并详细剖析了Spring循环依赖的底层处置打算。经过火析得悉:Spring自动会支持单例Bean的setter循环依赖,关于其余状况下的循环依赖,Spring自动是不支持的。并且,最后给出了Spring处置循环依赖的流程图。
七、思索
既然学完了,就开局思索几个疑问吧?
关于Spring的循环依赖,通常会有如下几个经典面试题:
Spring是如何解决循环依赖的?
你需要我,我需要你就是循环依赖
在Spring中使用的三级缓存来解决循环依赖问题,这里的缓存其实就是Map对象
当获取一个Bean时会先从缓存中查找是否有相应的Bean。
1创建A实例
2将A实例(半初始化,属性没有填充)暴露放入缓存中
3 填充A实例的属性
4 A实例属性依赖B对象
5 创建B对象实例
6 填充B实例属性
7 B实例属性依赖A对象
8 将上面已经暴露到三级缓存中的A对象注入给B实例
在获取A对象的时候执行上面27.1中的getSingleton方法,会将三级缓存中A这个半初始化状态的对象移除,将其存入到二级缓存中。
9 B实例Bean的创建工作继续执行初始化方法
B如果需要AOP代理?最终B对象是个代理对象。B到此就完全的初始化完了,B的依赖对象A此时是个半初始化状态的对象
10 B实例对象保存到一级缓存
最终B实例创建,初始化都执行完后会将自身加入到一级缓存同时清除二级,三级缓存
11 A实例Bean创建继续执行
如果B是被AOP代理的那么此时的A实例注入的B对象就是一个代理对象。
12A实例Bean执行初始化方法
13 A继续执行上面的10步骤
三级缓存解决问题:循环依赖+AOP问题
只用一,二级缓存:
从上面罗列的步骤看似乎很是完美解决了循环依赖问题,接下来我们看看加入AOP的场景
假如A,B两个对象最终都是要被AOP代理的
执行到这里,A中依赖的B是代理对象没有问题,但是B中依赖的A对象是原始对象;这就不正确了应该依赖的A也必须是代理对象才是。
引入三级缓存:
三级缓存引入了ObjectFactory对象,在获取对象的时候,是调用ObjectFactory#getObject方法。
而这个getObject方法的实现实际执行的是getEarlyBeanReference方法,再来回顾下:
在创建实例时先将其存入三级缓存中:
getEarlyBeanReference方法就是提前创建代理对象
如果开启了AOP代理后
通过getEarlyBeanReference方法提前创建代理对象。这样就解决了循环依赖时AOP代理问题。保证获取的都是同一个对象。
其实引入三级缓存还解决了一个问题就是延迟代理对象的创建,如果不应用ObjectFactory方式那么我们需要不管需不需要都要先创建代理对象,而引入ObjectFactory可以在注入的时候先暴露的是ObjectFactory只有在调用getObject方法的时候才去创建真正的代理对象(避免了所有Bean都强制创建代理对象)。当没有被代理时可以直接返回原始对象,如果被代理会提前创建代理对象。
不用二级直接是用一,三级缓存?
假设场景:A 依赖 B,B 依赖 A、C,C 依赖 A
如果这样会出现不同的代理对象,每次调用getObject都会创建不同的代理对象(在上面的场景中如果只用一,三级缓存那么 B 依赖 A会通过getObject获取一个代理对象Proxy$1,接着注入C的时候 C中又依赖A,那这时候又从getObject获取对象那么返回的将又会是一个新的代理对象Proxy$2;在这个过程中A对象就出现了2个不一样的对象了,这肯定是错误的)。而引入二级缓存也就解决了这个问题。只有二级缓存没有的时候才从三级缓存汇总获取(如果需要则创建代理对象,然后保存到二级缓存中,二级缓存中已经是提前创建了代理对象(如果需要代理))。
当一个Bean完全的创建完以后放入一级缓存中,此时会吧二级三级中的缓存清除。
完毕!!!!
SpringMVC参数统一验证方法 SpringBoot多数据源配置详解 SpringCloud Nacos 整合feign Spring AOP动态代理失效的解决方法@Transactional为何会失效 SpringBoot配置文件你了解多少? SpringBoot邮件发送示例 Springboot面试题整理附答案 SpringBoot项目查看线上日志 在Spring Cloud 中你还在使用Ribbon快来试试Load-Balancer SpringBoot分库分表sharding-sphere3
面试想吊打面试官?阿里架构师教你吃透Spring(Boot、Cloud、MVC)
作为一名Java程序员,我想没人敢小觑 Spring的重要性,现在出去面试,无论多小的公司 or 项目,都要跟你扯一扯 Spring,扯一扯微服务,如果啃不下来,很可能就与大厂失之交臂。
精通Spring的原理实现的话,可以帮助你更好地职业进阶,学习前辈优秀的架构设计思想,总结出最优使用方案,绕过工作中遇到的很多坑。
一个框架的源码也是最大的知识库,源码是一层一层嵌套的,光靠文字说明会比较难以理解,最好是在IDE环境下跟着一步一步跟着点下去。
如果要学习源码,最合适的就是Spring,理由如下:
面试常问Spring、Spring Boot、Spring Cloud、Spring MVC面试题解析
针对Spring、Spring Boot、Spring Cloud、Spring MVC,笔者这边整理了几份学习PDF,这些PDF是可以免费分享的!
收集整理不易有需要的朋友帮忙转发一下,然后 【点击这里】 获取免费领取方式!
需要此PDF文档的朋友们记得转发一下;然后 【点击这里】 获取免费领取方式!
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。