示例代码
<bean id="a" class="com.demo.circularreference.A">
<property name="b" ref="b"/>
</bean>
<bean id="b" class="com.demo.circularreference.B">
<property name="a" ref="a"/>
</bean>
public class A {
B b;
public void setB(B b) {
this.b = b;
}
}
public class B {
A a;
public void setA(A a) {
this.a = a;
}
}
Spring中将循环依赖的处理分为了3种情况
1. 构造器循环依赖
通过构造器注入形成的循环依赖是无法解决的,容器中会抛出BeanCurrentlyInCreationException异常。
首先需要明确的一点是,容器会标识一个当前正在创建的bean,将它记录在缓存singletonsCurrentlyInCreation中。
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** Names of beans that are currently in creation. */
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
当容器启动过程中,在DefaultSingletonBeanRegistry的getSingleton方法中,将bean id加入singletonsCurrentlyInCreation,如果加入失败则抛出BeanCurrentlyInCreationException。
/**
* Return the (raw) singleton object registered under the given name,
* creating and registering a new one if none registered yet.
* @param beanName the name of the bean
* @param singletonFactory the ObjectFactory to lazily create the singleton
* with, if necessary
* @return the registered singleton object
*/
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
//将bean id加入singletonsCurrentlyInCreation,如果加入失败则抛出IllegalStateException
beforeSingletonCreation(beanName);
//实例化单例bean
singletonObject = singletonFactory.getObject();
//将bean id从singletonsCurrentlyInCreation中移除,如果移除失败则抛出BeanCurrentlyInCreationException
afterSingletonCreation(beanName);
newSingleton = true;
if (newSingleton) {
//将实例化后的bean加入缓存singletonObjects,方便后续获取同一个单例bean时可以直接从缓存中返回
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
构造器注入和setter注入的不同之处在于,构造器注入的方式在通过构造方法实例化bean的时候就直接寻找依赖注入了,而setter注入需要在实例化步骤中先通过默认的无参构造方法实例化bean以后,再在后续方法中通过setter方法寻找依赖注入。这就导致通过构造器注入实例化A的时候,会直接去寻找依赖B,而依赖B又会去寻找依赖A,此时A和B的id都已经存在于缓存singletonsCurrentlyInCreation 中,当再次寻找A想加入缓存的时候,就会添加失败进而抛出BeanCurrentlyInCreationException异常。
2. setter循环依赖
一般是指单例bean之间能构成循环依赖。承接上文来说,当单例A实例化完成后会缓存在singletonFactories中。这样在setter注入B的时候,B需要去获取A,在doGetBean方法开头通过getSingleton中就可以直接返回。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
//实例化bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
//让实例化后的bean早早暴露出来,方便setter注入时依赖bean能提前发现依赖
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");
}
//加入到缓存singletonFactories中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Object exposedObject = bean;
//setter注入
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
return exposedObject;
}
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;
// 从singletonFactories中获取已经实例化的bean直接返回
Object sharedInstance = getSingleton(beanName);
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
//循环依赖的bean在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
//通过缓存singletonFactories直接返回
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
3. prototype范围的依赖处理
对于两个原型bean是无法处理循环依赖的。在doGetBean方法中就存在校验。
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;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// 发现原型bean的循环依赖抛出BeanCurrentlyInCreationException异常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
实际原因也很简单,原型bean不会被记录在缓存singletonFactory中。因为循环依赖中,当A去寻找B,B也会去寻找A,但是此时B找到的A不再是之前的A,而是一个新的A (prototype),这就导致这样的依赖关系没有终点,造成了死循环。
但是在某些场景下单例bean和原型bean是能构成循环依赖的。
singleton beanA和prototype beanB 两者构成循环依赖,当执行getBean方法执行存在先后顺序的时候,先通过getBean方法获取A的时候是可行的,但是先通过getBean方法获取B就不行了。因为先通过getBean方法获取A的时候,先实例化了单例A,再注入原型B,原型B中可以找到单例A,就能构成一次有终点的循环依赖。反之,先通过getBean方法获取B的时候,先实例化原型B,再注入单例A,单例A又依赖于一个新的原型B,这样就无法构成循环依赖了。因此在这个场景下,执行实例化的顺序十分重要。