Spring bean循环依赖问题,与解决方案。

前言

        咱们知道 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而后返回。

相关文章
相关标签/搜索