详解Spring循环依赖的解决方案

作者:数齐 时间:2022-05-29 13:14:57 

spring针对Bean之间的循环依赖,有自己的处理方案。关键点就是 * 缓存。当然这种方案不能解决所有的问题,他只能解决Bean单例模式下非构造函数的循环依赖。

我们就从A->B->C-A这个初始化顺序,也就是A的Bean中需要B的实例,B的Bean中需要C的实例,C的Bean中需要A的实例,当然这种需要不是构造函数那种依赖。前提条件有了,我们就可以开始了。毫无疑问,我们会先初始化A.初始化的方法是org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean


protected <T> T doGetBean(
  final String name, final Class<T> requiredType, 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); //关注点1
 if (sharedInstance != null && args == null) {
  if (logger.isDebugEnabled()) {
   if (isSingletonCurrentlyInCreation(beanName)) {
    logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
      "' that is not fully initialized yet - a consequence of a circular reference");
   }
   else {
    logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
   }
  }
  bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
 }

else {
  // Fail if we're already creating this bean instance:
  // We're assumably within a circular reference.
  if (isPrototypeCurrentlyInCreation(beanName)) {
   throw new BeanCurrentlyInCreationException(beanName);
  }

// Check if bean definition exists in this factory.
  BeanFactory parentBeanFactory = getParentBeanFactory();
  if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
   // Not found -> check parent.
   String nameToLookup = originalBeanName(name);
   if (args != null) {
    // Delegation to parent with explicit args.
    return (T) parentBeanFactory.getBean(nameToLookup, args);
   }
   else {
    // No args -> delegate to standard getBean method.
    return parentBeanFactory.getBean(nameToLookup, requiredType);
   }
  }

if (!typeCheckOnly) {
   markBeanAsCreated(beanName);
  }

try {
   final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
   checkMergedBeanDefinition(mbd, beanName, args);

// Guarantee initialization of beans that the current bean depends on.
   String[] dependsOn = mbd.getDependsOn();
   if (dependsOn != null) {
    for (String dependsOnBean : dependsOn) {
     if (isDependent(beanName, dependsOnBean)) {
      throw new BeanCreationException(mbd.getResourceDescription(), beanName,
        "Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'");
     }
     registerDependentBean(dependsOnBean, beanName);
     getBean(dependsOnBean);
    }
   }

// Create bean instance.
   if (mbd.isSingleton()) {
    //关注点2
    sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
     @Override
     public Object getObject() throws BeansException {
      try {
       return createBean(beanName, mbd, args);
      }
      catch (BeansException ex) {
       // Explicitly remove instance from singleton cache: It might have been put there
       // eagerly by the creation process, to allow for circular reference resolution.
       // Also remove any beans that received a temporary reference to the bean.
       destroySingleton(beanName);
       throw ex;
      }
     }
    });
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
   }

else if (mbd.isPrototype()) {
    // It's a prototype -> create a new instance.
    Object prototypeInstance = null;
    try {
     beforePrototypeCreation(beanName);
     prototypeInstance = createBean(beanName, mbd, args);
    }
    finally {
     afterPrototypeCreation(beanName);
    }
    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
   }

else {
    String scopeName = mbd.getScope();
    final Scope scope = this.scopes.get(scopeName);
    if (scope == null) {
     throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
    }
    try {
     Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
      @Override
      public Object getObject() throws BeansException {
       beforePrototypeCreation(beanName);
       try {
        return createBean(beanName, mbd, args);
       }
       finally {
        afterPrototypeCreation(beanName);
       }
      }
     });
     bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
    }
    catch (IllegalStateException ex) {
     throw new BeanCreationException(beanName,
       "Scope '" + scopeName + "' is not active for the current thread; consider " +
       "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
       ex);
    }
   }
  }
  catch (BeansException ex) {
   cleanupAfterBeanCreationFailure(beanName);
   throw ex;
  }
 }

// Check if required type matches the type of the actual bean instance.
 if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
  try {
   return getTypeConverter().convertIfNecessary(bean, requiredType);
  }
  catch (TypeMismatchException ex) {
   if (logger.isDebugEnabled()) {
    logger.debug("Failed to convert bean '" + name + "' to required type [" +
      ClassUtils.getQualifiedName(requiredType) + "]", ex);
   }
   throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
  }
 }
 return (T) bean;
}

这个方法很长我们一点点说。先看我们的关注点1 Object sharedInstance = getSingleton(beanName)根据名称从单例的集合中获取单例对象,我们看下这个方法,他最终是org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)


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 != NULL_OBJECT ? singletonObject : null);
}

大家一定要注意这个方法,很关键,我们开篇提到了 * 缓存,使用点之一就是这里。到底是哪 * 缓存呢,第一级缓存singletonObjects里面放置的是实例化好的单例对象。第二级earlySingletonObjects里面存放的是提前曝光的单例对象(没有完全装配好)。第 * singletonFactories里面存放的是要被实例化的对象的对象工厂。解释好了 * 缓存,我们再看看逻辑。第一次进来this.singletonObjects.get(beanName)返回的肯定是null。然后isSingletonCurrentlyInCreation决定了能否进入二级缓存中获取数据。


public boolean isSingletonCurrentlyInCreation(String beanName) {
 return this.singletonsCurrentlyInCreation.contains(beanName);
}

singletonsCurrentlyInCreation这个Set中有没有包含传入的BeanName,前面没有地方设置,所以肯定不包含,所以这个方法返回false,后面的流程就不走了。getSingleton这个方法返回的是null。

下面我们看下关注点2.也是一个getSingleton只不过他是真实的创建Bean的过程,我们可以看到传入了一个匿名的ObjectFactory的对象,他的getObject方法中调用的是createBean这个真正的创建Bean的方法。当然我们可以先搁置一下,继续看我们的getSingleton方法


public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
 Assert.notNull(beanName, "'beanName' must not be null");
 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 the singletons of this factory are in destruction " +
      "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
   }
   if (logger.isDebugEnabled()) {
    logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
   }
   beforeSingletonCreation(beanName);
   boolean newSingleton = false;
   boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
   if (recordSuppressedExceptions) {
    this.suppressedExceptions = new LinkedHashSet<Exception>();
   }
   try {
    singletonObject = singletonFactory.getObject();
    newSingleton = true;
   }
   catch (IllegalStateException ex) {
    // Has the singleton object implicitly appeared in the meantime ->
    // if yes, proceed with it since the exception indicates that state.
    singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null) {
     throw ex;
    }
   }
   catch (BeanCreationException ex) {
    if (recordSuppressedExceptions) {
     for (Exception suppressedException : this.suppressedExceptions) {
      ex.addRelatedCause(suppressedException);
     }
    }
    throw ex;
   }
   finally {
    if (recordSuppressedExceptions) {
     this.suppressedExceptions = null;
    }
    afterSingletonCreation(beanName);
   }
   if (newSingleton) {
    addSingleton(beanName, singletonObject);
   }
  }
  return (singletonObject != NULL_OBJECT ? singletonObject : null);
 }
}

这个方法的第一句Object singletonObject = this.singletonObjects.get(beanName)从一级缓存中取数据,肯定是null。随后就调用的beforeSingletonCreation方法。


protected void beforeSingletonCreation(String beanName) {
 if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
  throw new BeanCurrentlyInCreationException(beanName);
 }
}

其中就有往singletonsCurrentlyInCreation这个Set中添加beanName的过程,这个Set很重要,后面会用到。随后就是调用singletonFactory的getObject方法进行真正的创建过程,下面我们看下刚刚上文提到的真正的创建的过程createBean,它里面的核心逻辑是doCreateBean.


protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
 // Instantiate the bean.
 BeanWrapper instanceWrapper = null;
 if (mbd.isSingleton()) {
  instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
 }
 if (instanceWrapper == null) {
  instanceWrapper = createBeanInstance(beanName, mbd, args);
 }
 final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
 Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);

// Allow post-processors to modify the merged bean definition.
 synchronized (mbd.postProcessingLock) {
  if (!mbd.postProcessed) {
   applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
   mbd.postProcessed = true;
  }
 }

// Eagerly cache singletons to be able to resolve circular references
 // even when triggered by lifecycle interfaces like BeanFactoryAware.
 //关注点3
 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, new ObjectFactory<Object>() {
   @Override
   public Object getObject() throws BeansException {
    return getEarlyBeanReference(beanName, mbd, bean);
   }
  });
 }

// Initialize the bean instance.
 Object exposedObject = bean;
 try {
  populateBean(beanName, mbd, instanceWrapper);
  if (exposedObject != null) {
   exposedObject = initializeBean(beanName, exposedObject, mbd);
  }
 }
 catch (Throwable ex) {
  if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
   throw (BeanCreationException) ex;
  }
  else {
   throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
  }
 }

if (earlySingletonExposure) {
  Object earlySingletonReference = getSingleton(beanName, false);
  if (earlySingletonReference != null) {
   if (exposedObject == bean) {
    exposedObject = earlySingletonReference;
   }
   else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
    String[] dependentBeans = getDependentBeans(beanName);
    Set<String> actualDependentBeans = new LinkedHashSet<String>(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 " +
       "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
    }
   }
  }
 }

// Register bean as disposable.
 try {
  registerDisposableBeanIfNecessary(beanName, bean, mbd);
 }
 catch (BeanDefinitionValidationException ex) {
  throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
 }

return exposedObject;
}

createBeanInstance利用反射创建了对象,下面我们看看关注点3 earlySingletonExposure属性值的判断,其中有一个判断点就是isSingletonCurrentlyInCreation(beanName)


public boolean isSingletonCurrentlyInCreation(String beanName) {
 return this.singletonsCurrentlyInCreation.contains(beanName);
}

发现使用的是singletonsCurrentlyInCreation这个Set,上文的步骤中已经将BeanName已经填充进去了,所以可以查到,所以earlySingletonExposure这个属性是结合其他的条件综合判断为true,进行下面的流程addSingletonFactory,这里是为这个Bean添加ObjectFactory,这个BeanName(A)对应的对象工厂,他的getObject方法的实现是通过getEarlyBeanReference这个方法实现的。首先我们看下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);
  }
 }
}

往第 * 缓存singletonFactories存放数据,清除第二级缓存根据beanName的数据。这里有个很重要的点,是往 * 缓存里面set了值,这是Spring处理循环依赖的核心点。getEarlyBeanReference这个方法是getObject的实现,可以简单认为是返回了一个为填充完毕的A的对象实例。设置完 * 缓存后,就开始了填充A对象属性的过程。下面这段描述,没有源码提示,只是简单的介绍一下。

填充A的时候,发现需要B类型的Bean,于是继续调用getBean方法创建,记性的流程和上面A的完全一致,然后到了填充C类型的Bean的过程,同样的调用getBean(C)来执行,同样到了填充属性A的时候,调用了getBean(A),我们从这里继续说,调用了doGetBean中的Object sharedInstance = getSingleton(beanName),相同的代码,但是处理逻辑完全不一样了。


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 != NULL_OBJECT ? singletonObject : null);
}

还是从singletonObjects获取对象获取不到,因为A是在singletonsCurrentlyInCreation这个Set中,所以进入了下面的逻辑,从二级缓存earlySingletonObjects中取,还是没有查到,然后从 * 缓存singletonFactories找到对应的对象工厂调用getObject方法获取未完全填充完毕的A的实例对象,然后删除 * 缓存的数据,填充二级缓存的数据,返回这个对象A。C依赖A的实例填充完毕了,虽然这个A是不完整的。不管怎么样C式填充完了,就可以将C放到一级缓存singletonObjects同时清理二级和 * 缓存的数据。同样的流程B依赖的C填充好了,B也就填充好了,同理A依赖的B填充好了,A也就填充好了。Spring就是通过这种方式来解决循环引用的。

来源:https://www.jianshu.com/p/16a44c25c9d9

标签:Spring,循环依赖
0
投稿

猜你喜欢

  • java利用udp实现发送数据

    2023-03-21 18:30:49
  • 基于Java8实现提高Excel读写效率

    2023-11-25 10:01:37
  • Java实现简单台球游戏

    2022-06-28 23:55:59
  • Mybatis-Plus查询中如何排除标识字段

    2023-11-23 20:38:46
  • 基于java实现租车管理系统

    2022-02-08 12:48:49
  • Java实现解出世界最难九宫格问题

    2022-06-14 19:47:10
  • Java求两集合中元素交集的四种方法对比分析

    2023-08-23 09:24:56
  • 聊聊Java的switch为什么不支持long

    2023-08-24 17:35:14
  • java源码解析之String类的compareTo(String otherString)方法

    2023-11-11 23:10:00
  • Spring Batch批处理框架使用解析

    2021-12-24 03:41:19
  • Java自定义注解用法实例小结

    2023-03-26 09:13:51
  • 手动模拟JDK动态代理的方法

    2023-03-23 00:49:03
  • 深入理解java自旋锁

    2023-10-28 00:32:49
  • MyBatis3源码解析之如何获取数据源详解

    2023-12-06 02:23:08
  • feign 如何获取请求真实目的ip地址

    2021-08-13 15:47:49
  • Java日常练习题,每天进步一点点(2)

    2023-08-17 22:46:19
  • IDEA入门级使用教程你居然还在用eclipse?

    2022-03-27 07:35:19
  • java 数据结构单链表的实现

    2022-07-24 09:45:33
  • 基于Spring整合mybatis注解扫描是否成功的问题

    2023-01-23 18:29:22
  • Java事务管理学习之Spring和Hibernate详解

    2023-04-11 00:01:25
  • asp之家 软件编程 m.aspxhome.com