咱们知道 Spring 能够是懒加载的,就是当真正使用到 Bean 的时候才实例化 Bean。固然也不全是这样,例如配置 Bean 的 lazy-init 属性,能够控制 Spring 的加载时机。如今机器的性能、内存等都比较高,基本上也不使用懒加载,在容器启动时候来加载bean,启动时间稍微长一点儿,这样在实际获取 bean 供业务使用时,就能够减轻很多负担,这个后面再作分析。 咱们使用到 Bean 的时候,最直接的方式就是从 Factroy 中获取,这个就是加载 Bean 实例的源头。java
最近发现一个问题,一些大的公司(国内知名的boss级别公司就那么几家),在面试的过程当中,会问到一个基础题:spring怎么实现循环依赖,或者循环依赖的解决方案。今天主要就这个问题作下简单的探讨:web
3个简单的bean:TestA,TestB,TestC其中A包含B,B含C,C含A。而后把三类注入到spring容器中。操做以下:面试
A类:spring
public class TestA { private TestB testB; public TestB getTestB() { return testB; } public void setTestB(TestB testB) { this.testB = testB; } }
B类:并发
public class TestB { private TestC testC; public TestC getTestC() { return testC; } public void setTestC(TestC testC) { this.testC = testC; } }
C类:app
public class TestC { private TestA testA; public TestA getTestA() { return testA; } public void setTestA(TestA testA) { this.testA = testA; } }
简单的注入到spring中xml如:性能
<!-- 循环依赖测试 --> <bean id="testA" class="com.xin.learn.xhyl.vo.TestA" scope="prototype"> <property name="testB" ref="testB"></property> </bean> <bean id="testB" class="com.xin.learn.xhyl.vo.TestB" scope="prototype"> <property name="testC" ref="testC"></property> </bean> <bean id="testC" class="com.xin.learn.xhyl.vo.TestC" scope="prototype"> <property name="testA" ref="testA"></property> </bean>
测试类:测试
public class TestMain { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/applicationContext.xml"); System.out.println(context.getBean("testA", TestA.class)); } }
若是你是web项目,运行项目不会报错,可是当你引用的时候,或者运行测试类后发现报错:ui
Error creating bean with name 'testA' defined in class path resource [spring/applicationContext.xml]: Cannot resolve reference to bean 'testB' while setting bean property 'testB'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testB' defined in class path resource [spring/applicationContext.xml]: Initialization of bean failed; nested exception is org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type 'com.xin.learn.xhyl.vo.TestB' to required type 'com.xin.learn.xhyl.vo.TestC' for property 'testC'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [com.xin.learn.xhyl.vo.TestB] to required type [com.xin.learn.xhyl.vo.TestC] for property 'testC': no matching editors or conversion strategy found
大体意思是:在建立testA的时,设置属性testB的时候不能引用testB。
由于这个时候的testB尚未被建立。this
解决:当把 scope的值改成singleton时,或者去掉scope(因spring默认的bean就是单例的),运行就正常了。缘由:
Spring提供了EarlyBeanReference功能,首先Spring里面有个名字为singletonObjects的并发map用来存放全部实例化而且初始化好的bean,singletonFactories则用来存放须要解决循环依赖的bean信息(beanName,和一个回调工厂)。当实例化beanA时候会触发getBean(“beanA”);
首先看singletonObjects中是否有beanA有则返回,一开始确定没有因此会实例化beanA,若是设置了allowCircularReferences=true
(默认为true)而且当前bean为单件而且该bean目前在建立中,则初始化属性前把该bean信息放入singletonFactories单件map里面。而后对该实例进行属性注入beanB,属性注入时候会getBean(“beanB”)
,发现beanB 不在singletonObjects中,就会实例化beanB,而后放入singletonFactories,而后进行属性注入beanA,而后触发getBean(“beanA”);
这时候会到(1)getSingleton返回实例化的beanA。到此beanB初始化完毕添加beanB 到singletonObjects而后返回,而后beanA 初始化完毕,添加beanA到singletonObjects而后返回。