简介
如下图所示,A中引用了B,B中引用了A,这样的两个以上的bean之间互相引用的情况就叫循环依赖。

测试
测试代码
public class A {
private B b;
public A() {
}
public A(B b) {
this.b = b;
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
public class B {
private A a;
public B() {
}
public B(A a) {
this.a = a;
}
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
@Test
public void test19() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
A a = (A) context.getBean("a");
B b = (B) context.getBean("b");
System.out.println(a);
System.out.println(b);
System.out.println(a.getB() == b);
}
构造器注入
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="a" class="mncode.A">
<constructor-arg ref="b"/>
</bean>
<bean id="b" class="mncode.B">
<constructor-arg ref="a"/>
</bean>
</beans>
会报异常:BeanCurrentlyInCreationException
属性注入
修改配置文件:
<bean id="a" class="mncode.A">
<property name="b" ref="b"/>
</bean>
<bean id="b" class="mncode.B">
<property name="a" ref="a"/>
</bean>
输出结果:
mncode.A@42eca56e
mncode.B@52f759d7
true
prototype模式
Spring中的bean默认为singleton模式,如果改为prototype模式,如下所示:
<bean id="a" class="mncode.A" scope="prototype">
<property name="b" ref="b"/>
</bean>
<bean id="b" class="mncode.B" scope="prototype">
<property name="a" ref="a"/>
</bean>
会报异常:BeanCurrentlyInCreationException
总结
Spring不能解决构造器循环依赖;Spring不能解决prototype模式的循环依赖;Spring可以解决singleton模式的属性循环依赖;
源码分析
依赖注入在第一次调用getBean方法时触发,时序图如下:

1、在第7步:doCreateBean中A实例化之后,有以下代码:
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
addSingletonFactory源码如下:
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
A实例化之后放在了singletonFactories缓存中。
2、然后进入第10步:作属性注入时,发现A引用了B,使用getBean去获取B,在getSingleton方法中有以下源码:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
先从singletonObjects缓存中获取,再从earlySingletonObjects缓存中获取,最后从singletonFactories缓存中获取,因为都没有,返回了null;所以需要去创建B,实例化之后同样也放在singletonFactories缓存中;
3、B的流程进入第10步作属性注入,发现引用了A,使用getBean去获取A,同上也是先从3个缓存中找,从singletonObjects缓存找到了;然后将A从singletonFactories缓存中移到earlySingletonObjects缓存中;
4、B的流程进入第13步,addSingleton源码如下:
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
B从singletonFactories或earlySingletonObjects缓存中移到singletonObjects缓存中。
5、B的创建流程结束,继续进行A的后半部分流程;
6、A的流程进入第13步,同上,A从singletonFactories或earlySingletonObjects缓存中移到singletonObjects缓存中。
7、A的创建流程结束。
总结
- Spring中使用了三级缓存的结构:
singletonObjects(第一级)、earlySingletonObjects(第二级)、singletonFactories(第三级); bean实例化后属性注入前会提前曝光,放入第三级缓存中,这样其他地方依赖这个bean时可以找到(虽然此时bean并不完整,但在堆中已经分配了空间,可以通过引用找到);bean创建流程走完时会移到第一级缓存中,此时bean是完整的(其他地方引用的这个bean自然也会是完整的)。
备注
| 变量 | 类型 | 说明 |
|---|---|---|
singletonObjects | ConcurrentHashMap | beanName ---> bean instance |
earlySingletonObjects | HashMap | beanName ---> bean instance(这里的 bean 不是完整的) |
singletonFactories | HashMap | beanName ---> ObjectFactory |
alreadyCreated | Set | Names of beans that have already been created at least once |
registeredSingletons | LinkedHashSet | 注册过的bean名称集合 |
singletonsCurrentlyInCreation | Set | 正在创建中的bean名称集合 |
factoryBeanInstanceCache | ConcurrentHashMap | Cache of unfinished FactoryBean instances: FactoryBean name to BeanWrapper |