一、什么是循环依赖?
循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。如下图:
二、官方解释
通过构造器注入,无法解决循环依赖问题,采用setter方式是可以的。
三、循环依赖场景还原
Spring容器默认是使用单例的,单例是支持循环依赖的,不会报错,原型模式是不支持的,会报错。
public class TestA {
private TestB testB;
public TestB getTestB() {
return testB;
}
public void setTestB(TestB testB) {
this.testB = testB;
}
public TestA() {
System.out.println("TestA-->构造函数");
}
}
public class TestB {
private TestA testA;
public TestA getTestA() {
return testA;
}
public void setTestA(TestA testA) {
this.testA = testA;
}
public TestB() {
System.out.println("TestB-->构造函数");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--
scope="singleton" 单例模式是正常的,不会报错,已经解决了依赖循环
scope="prototype" 原型模式是不支持,执行代码时会报错
-->
<bean id="testA" class="com.guocoffee.spring.circular.TestA" scope="prototype">
<property name="testB" ref="testB"></property>
</bean>
<bean id="testB" class="com.guocoffee.spring.circular.TestB" scope="prototype">
<property name="testA" ref="testA"></property>
</bean>
</beans>
public class test {
public static void main(String[] args) {
ApplicationContext aop = new ClassPathXmlApplicationContext("circular.xml");
TestA testA = aop.getBean(TestA.class);
TestB testB = aop.getBean(TestB.class);
}
}
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testA' defined in class path resource [circular.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 [circular.xml]:
Cannot resolve reference to bean 'testA' while setting bean property 'testA';
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'testA': Requested bean is currently in creation:
Is there an unresolvable circular reference?
四、Spring怎么解决循环依赖
源码类名:DefaultSingletonBeanRegist
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
private static final int SUPPRESSED_EXCEPTIONS_LIMIT = 100;
// 第一级缓存(singletonObjects):也叫单例池,用于存放完全初始化好的 bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
// 第三级缓存(singletonFactories):单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
// 二级缓存(earlySingletonObjects):提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
private final Map<String, Object> earlySingletonObjects = new HashMap(16);
单例的bean是通过三级缓存提前暴露来解决循环依赖的,而非单例的bean(原型),每次从容器中获取到一个新对象,是没有缓存的,是不会存在三级缓存中的。
五、源码解析
实例化:只是申请了一块内存空间(相当于毛坯房)
初始化:完成属性的赋值(装修、家电)