Spring 循环依赖问题

示例代码

<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,这样就无法构成循环依赖了。因此在这个场景下,执行实例化的顺序十分重要。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值