spring如何解决循环依赖的

261 阅读2分钟

spring能够解决循环依赖,前提是单例bean,同时allowCircularReferences为true

refresh的时候(容器初始化)最终调用

/** Whether to automatically try to resolve circular references between beans. */
private boolean allowCircularReferences = true;
默认true
public Object getSingleton(String beanName) {
   return getSingleton(beanName, true);
}
默认allowEarlyReference是true支持循环依赖

通常我们说spring是通过三级缓存解决的循环依赖问题,这三级缓存其实就是在解决循环依赖的时候三个重要的map

map说明
singletonObjects单例池,存储完整的bean
earlySingletonObjects早期的单例池,存储不完整的bean,如果需要进行APO,则直接放入从 singletonFactories生成代理对象
singletonFactories单例工厂,用于生成早期的bean

如何判断是否是循环依赖?

public boolean isSingletonCurrentlyInCreation(String beanName) {
   return this.singletonsCurrentlyInCreation.contains(beanName);
}
singletonsCurrentlyInCreation 是一个SET,用于判断一个bean是否在创建中

什么时候singletonsCurrentlyInCreation放入的beanName?

protected void beforeSingletonCreation(String beanName) {
   if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
   }
}
在doGetbean的时候通过下面的代码进行创建bean

sharedInstance = getSingleton(beanName, () -> {
    try {
            return createBean(beanName, mbd, args);
    }
    catch (BeansException ex) {
        destroySingleton(beanName);
        throw ex;
    }
    });
在getSingleton中调用createBean之前调用了beforeSingletonCreation进行放入的,到这里我们基本明白了什么时候放入的了

什么时候放入三级缓存singletonFactories?

if (earlySingletonExposure) {
   addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
调用addSingletonFactory 放入三级缓存,其中参数是一个lambda
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}

什么时候放入二级缓存earlySingletonObjects?

Object earlySingletonReference = getSingleton(beanName, false);
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
    singletonObject = singletonFactory.getObject();
    this.earlySingletonObjects.put(beanName, singletonObject);
    this.singletonFactories.remove(beanName);
}

当出现循环依赖的时候singletonFactory已经在刚开始的时候放入了,这里不是空,直接把bean工厂产生的对象,如果有代理的话则通过下面的方式生成代理对象
public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    this.earlyProxyReferences.put(cacheKey, bean);
    return wrapIfNecessary(bean, beanName, cacheKey);
}
放入到了早期的SingletonObjects
这里的earlyProxyReferences 也很重要,防止后面部分的aop动作重复执行postProcessAfterInitialization

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

什么时候放入一级缓存singletonObjects?

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);
   }
}

什么时候三级缓存无法解决循环依赖呢? 有一种情况,就是自己实现了BeanPostProcessor的后置处理器,这里会从新生成一个代理对象导致最后的执行步骤,源码如下

if (earlySingletonReference != null) {
   if (exposedObject == bean) {
      exposedObject = earlySingletonReference;
   }
   else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
      String[] dependentBeans = getDependentBeans(beanName);
      Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
      for (String dependentBean : dependentBeans) {
         if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
            actualDependentBeans.add(dependentBean);
         }
      }
      if (!actualDependentBeans.isEmpty()) {
         throw new BeanCurrentlyInCreationException(beanName,
               "Bean with name '" + beanName + "' has been injected into other beans [" +
               StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
               "] in its raw version as part of a circular reference, but has eventually been " +
               "wrapped. This means that said other beans do not use the final version of the " +
               "bean. This is often the result of over-eager type matching - consider using " +
               "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
      }
   }
}

最终 exposedObject == bean 会是fasles,因为、exposedObject = initializeBean(beanName, exposedObject, mbd);这里会调用后置处理器处理bean,如果这里对bean进了代理,和原来的bean就不一样了,导致循环依赖异常