简介
如下图所示,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 |