循环依赖
所谓循环依赖,是指在spring中,类A
中有一个成员变量为类B
,而类B
中也有一个成员变量为类A
。这会造成在spring初始化容器中的bean时,bean A的初始化需要bean B的初始化的完成,而bean B的初始化又需要bean A初始化的完成。
A与B间形成了循环依赖:
循环依赖的解决
spring中,循环依赖的解决是有条件的:仅限于singleton(单例)作用域的bean,且依赖的成员变量不是在构造函数中初始化的。
不能解决的情况一:
原型prototype
作用域的bean发生循环依赖会直接抛出异常。
/**AbstractBeanFactory.doGetBean()方法*/
// 如果原型作用域的bean在获取时,发现正在创建中
// 即发生了循环依赖,直接抛出异常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
这种情况好理解,原型的bean如果允许循环依赖,那一定会处在不断创建依赖类的循环中,最终导致堆溢出
不能解决的情况二:
单例bean,但依赖的成员变量在构造函数中初始化。如:
public class A {
private B b;
public A(B b) {
this.b = b;
}
}
public class B {
@Autowired
private A a;
}
//配置文件:
<bean id="a" class="test.spring.bean.A" >
<constructor-arg ref="b" index="0" />
</bean>
<bean id="b" class="test.spring.bean.B" />
简单说明一下原因,spring中的循环依赖的解决关键在于:
缓存 实例化成功,但还没注入成员变量的bean,等发生循环依赖时,直接引用缓存中只实例化过的bean,这样就不用等待依赖bean继续注入成员变量,然后发生循环依赖。
如果实例化过程中就发生了循环依赖,还没来得及缓存,那就只有抛出异常了。
循环依赖的解决:
当我们调用context.getBean()
方法来获取一个单例bean时,如果这个bean在容器中还没有,会进行创建(AbstractAutowireCapableBeanFactory.createBean()
)。
创建这个bean的过程简单可分为两步:实例化(instantiate,使用构造函数创建bean对象)和初始化(initialize,注入bean的成员变量):
1. 存入map缓存
当实例化完后,会将构造好的bean对象存在singletonFactories
缓存map中,见AbstractAutowireCapableBeanFactory.doCreateBean()
:
// 如果bean为单例且允许循环依赖,将该bean的singletonFactory存在map中暴露
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
//重点!!!
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
addSingletonFactory()
方法中,beanName-ObjectFactory
这样的key-value
被放进了singletonFactories
map缓存集合。
这里注意,实例化的bean并不是直接存入缓存map,而是通过ObjectFactory
工厂对象被暴露在了缓存里,后面循环依赖时,也是以ObjectFactory
形式被取出。然后调用ObjectFactory.getObject()
——》getEarlyBeanReference()
方法,来获取到被SmartInstantiationAwareBeanPostProcessor
后处理器处理过的刚刚实例化结束的bean对象。
这里的ObjectFactory
发生过替换。bean在第一次获取时,生成的ObjectFactory对象,getObject()
方法实际上会调用createBean()
方法创建bean。而bean在创建的过程中,实例化完成后会重新创建ObjectFactory对象,getObject()
方法会变成调用getEarlyBeanReference()
,用于获取的缓存的实例化的bean。
2. 循环依赖时,从缓存中取出
当实例化bean A存入缓存后,我们初始化A ,这时会注入依赖类B(getBean()
方法)。
依赖类B注入时进行创建,实例化——》初始化时,又循环依赖了bean A,但这时,缓存中已经保存了实例化的bean A,我们通过getSingleton()
方法直接获取A对象注入后返回。循环依赖被破解。
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// 从缓存中获取单例bean,缓存中的bean只被实例化,还未初始化
Object sharedInstance = getSingleton(beanName);
//获取成功,直接返回bean
if (sharedInstance != null && args == null) {
…………
}
………………
}
//从singletonFactories中获取该beanName的ObjectFactory,然后通过ObjectFactory获取bean
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;
}
3. 整个流程
最后梳理下整个循环依赖解决的流程,以上面的A、B为例:
-
context.getBean(A.class)
获取容器中的单例A,此时bean A对象还不存在,进行创建。 - 实例化bean A,将其放入缓存
- 初始化A,注入依赖类B
- 通过
getBean(B.class)
获取依赖类B,发现bean B在容器中不存在,进行创建 - 实例化B,将其放入缓存
- 初始化B,注入依赖类A
- 通过
getBean(A.class)
获取依赖类A,此时发现缓存中已经存在实例化的A,获取后返回 - B初始化成功,返回
- A初始化成功, 返回bean A
注意:整个流程结束后,B持有的也不再是实例化的A,而是初始化结束的A。