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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_18537055/article/details/98681106

前言

        我们知道 Spring 可以是懒加载的,就是当真正使用到 Bean 的时候才实例化 Bean。当然也不全是这样,例如配置 Bean 的 lazy-init 属性,可以控制 Spring 的加载时机。现在机器的性能、内存等都比较高,基本上也不使用懒加载,在容器启动时候来加载bean,启动时间稍微长一点儿,这样在实际获取 bean 供业务使用时,就可以减轻不少负担,这个后面再做分析。 我们使用到 Bean 的时候,最直接的方式就是从 Factroy 中获取,这个就是加载 Bean 实例的源头。

        最近发现一个问题,一些大的公司(国内知名的boss级别公司就那么几家),在面试的过程中,会问到一个基础题:spring怎么实现循环依赖,或者循环依赖的解决方案。今天主要就这个问题做下简单的探讨:

        3个简单的bean:TestA,TestB,TestC其中A包含B,B含C,C含A。然后把三类注入到spring容器中。操作如下:

A类:

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类:

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项目,运行项目不会报错,但是当你引用的时候,或者运行测试类后发现报错:

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还没有被创建。

解决:当把 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然后返回。

猜你喜欢

转载自blog.csdn.net/qq_18537055/article/details/98681106