zoukankan      html  css  js  c++  java
  • Spring解决bean之间的循环依赖

    转自链接:https://blog.csdn.net/lyc_liyanchao/article/details/83099675
    通过前几节的分析,已经成功将bean实例化,但是大家一定要将bean的实例化和完成bean的创建区分开,bean的实例化仅仅是获得了bean的实例,该bean仍在继续创建之中,之后在该bean实例的基础之上,还要做很多额外的操作,例如bean的属性填充、处理器的应用、bean的循环依赖解决等,今天我们就来分析下Spring是如何解决bean之间的循环依赖。

    当ClassA引用ClassB,ClassB又引用ClassA,那么两个类之间就会形成一个闭环,导致循环依赖的出现。大家只需记住一点,Spring只能解决单例模式下的Setter循环依赖。

    1.测试用例
    bean和xml 

     1 package com.lyc.cn.v2.day01.cycle;
     2 
     3 /**
     4  * @author: LiYanChao
     5  * @create: 2018-10-16 23:59
     6  */
     7 public class ClassA {
     8     private ClassB classB;
     9 
    10     public ClassB getClassB() {
    11         return classB;
    12     }
    13 
    14     public void setClassB(ClassB classB) {
    15         this.classB = classB;
    16     }
    17 }
     1 package com.lyc.cn.v2.day01.cycle;
     2 
     3 /**
     4 * @author: LiYanChao
     5 * @create: 2018-10-16 23:59
     6 */
     7 public class ClassB {
     8 private ClassA classA;
     9 
    10 public ClassA getClassA() {
    11 return classA;
    12 }
    13 
    14 public void setClassA(ClassA classA) {
    15 this.classA = classA;
    16 }
    17 }
    <!--循环依赖-->
    <bean id="classA" class="com.lyc.cn.v2.day01.cycle.ClassA" scope="singleton">
    <property name="classB" ref="classB"></property>
    </bean>
    <bean id="classB" class="com.lyc.cn.v2.day01.cycle.ClassB" scope="singleton">
    <property name="classA" ref="classA"></property>
    </bean>

    结果

    ========测试方法开始=======
    
    com.lyc.cn.v2.day01.cycle.ClassB@2d6a9952
    com.lyc.cn.v2.day01.cycle.ClassA@22a71081
    
    ========测试方法结束=======

    当scope="singleton"时结果是正常的,Spring为我们解决了bean之间的循环依赖,再将scope改为prototype,运行测试用例(摘取部分异常信息):

    Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'classA': Requested bean is currently in creation: Is there an unresolvable circular reference?
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:255)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:198)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:372)
    ... 40 more

    从异常信息中可以看到Is there an unresolvable circular reference?,有循环依赖异常,这也证明了Spring是不能解决prototype作用域的bean之间的循环依赖的。

    下面我们从源码角度去分析,Spring是如何解决bean之间的循环依赖问题的。

      1 protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
      2 
      3     // Instantiate the bean.
      4     // ① 实例化bean
      5     BeanWrapper instanceWrapper = null;
      6     // 注意factoryBeanInstanceCache是ConcurrentMap,remove方法会返回删除的键值(如果不存在返回null)
      7     if (mbd.isSingleton()) {
      8         instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
      9     }
     10     // 如果factoryBeanInstanceCache没有缓存对应的BeanWrapper,则重新创建bean实例
     11     if (instanceWrapper == null) {
     12         instanceWrapper = createBeanInstance(beanName, mbd, args);
     13     }
     14     final Object bean = instanceWrapper.getWrappedInstance();
     15     Class<?> beanType = instanceWrapper.getWrappedClass();
     16     if (beanType != NullBean.class) {
     17         mbd.resolvedTargetType = beanType;
     18     }
     19 
     20     // Allow post-processors to modify the merged bean definition.
     21     // ② 允许MergedBeanDefinitionPostProcessor后处理器修改已合并的bean定义。
     22     synchronized (mbd.postProcessingLock) {
     23         if (!mbd.postProcessed) {
     24             try {
     25                 applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
     26             }
     27             catch (Throwable ex) {
     28                 throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex);
     29             }
     30             mbd.postProcessed = true;
     31         }
     32     }
     33 
     34     // Eagerly cache singletons to be able to resolve circular references
     35     // even when triggered by lifecycle interfaces like BeanFactoryAware.
     36     // ③ 提前缓存ObjectFactory以解决bean之间的循环依赖
     37     // mbd.isSingleton()->是否单例,Spring只解决单例bean的循环依赖问题
     38     // allowCircularReferences->是否允许循环依赖
     39     // isSingletonCurrentlyInCreation->该bean是否创建中
     40     boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
     41     if (earlySingletonExposure) {
     42         addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
     43     }
     44 
     45     // Initialize the bean instance.
     46     // ④ 初始化bean实例 这里大家要与第①步区分开,到这里bean已经完成了实例化,但是还没有完成初始化的操作,例如bean的属性填充
     47     Object exposedObject = bean;
     48     try {
     49         // 填充bean属性
     50         populateBean(beanName, mbd, instanceWrapper);
     51         // 初始化bean
     52         exposedObject = initializeBean(beanName, exposedObject, mbd);
     53     }
     54     catch (Throwable ex) {
     55         if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
     56             throw (BeanCreationException) ex;
     57         }
     58         else {
     59             throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
     60         }
     61     }
     62 
     63     // ⑤ 循环依赖检查
     64     if (earlySingletonExposure) {
     65         Object earlySingletonReference = getSingleton(beanName, false);
     66         if (earlySingletonReference != null) {
     67             if (exposedObject == bean) {
     68                 exposedObject = earlySingletonReference;
     69             }
     70             else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
     71                 String[] dependentBeans = getDependentBeans(beanName);
     72                 Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
     73                 for (String dependentBean : dependentBeans) {
     74                     if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
     75                         actualDependentBeans.add(dependentBean);
     76                     }
     77                 }
     78                 if (!actualDependentBeans.isEmpty()) {
     79                     throw new BeanCurrentlyInCreationException(beanName,
     80                             "Bean with name '" + beanName + "' has been injected into other beans [" +
     81                             StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
     82                             "] in its raw version as part of a circular reference, but has eventually been " +
     83                             "wrapped. This means that said other beans do not use the final version of the " +
     84                             "bean. This is often the result of over-eager type matching - consider using " +
     85                             "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
     86                 }
     87             }
     88         }
     89     }
     90 
     91     // Register bean as disposable.
     92     try {
     93         // ⑥ 根据bean的作用域注册bean
     94         registerDisposableBeanIfNecessary(beanName, bean, mbd);
     95     }
     96     catch (BeanDefinitionValidationException ex) {
     97         throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
     98     }
     99     // ⑦ 返回bean实例
    100     return exposedObject;
    101 }

    通过第一步已经获得了bean的实例(第二步留在以后再讲解),直接看第三步:提前缓存ObjectFactory以解决bean之间的循环依赖。

    1.提前曝光对象
    这里涉及到一个非常重要的接口ObjectFactory,该接口是一个函数式接口且只有一个方法:T getObject() throws BeansException;,该方法用于返回一个bean的实例,此时的bean已经完成初始化,但是尚未完成创建。

    如果当前的bean满足条件,则将当前正在创建的bean和其ObjectFactory对象提前曝光,加入到正在创建bean池中。

    1 protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    2     synchronized (this.singletonObjects) {
    3         if (!this.singletonObjects.containsKey(beanName)) {
    4             this.singletonFactories.put(beanName, singletonFactory);
    5             this.earlySingletonObjects.remove(beanName);
    6             this.registeredSingletons.add(beanName);
    7         }
    8     }
    9 }

    2.循环依赖的解决
    在完成bean的实例创建之后,还要填充bean的属性,针对ClassA,其属性是ClassB,如果要填充ClassA的属性则势必先要实例化ClassB,那么这里又涉及到一个概念,RuntimeBeanReference–>运行时引用。

    打开BeanDefinitionValueResolver类的resolveValueIfNecessary方法。摘取代码片段(该方法会在以后全部分析)

    判断RuntimeBeanReference属性
     

    1 // ① RuntimeBeanReference->运行时引用
    2 //   例如BeanA依赖BeanB,那么在配置文件中有通过配置ref标签进行引用的,在解析BeanDefinition的时候,是不会直接实例化BeanB的,那么这个引用就是RuntimeBeanReference
    3 if (value instanceof RuntimeBeanReference) {
    4     RuntimeBeanReference ref = (RuntimeBeanReference) value;
    5     return resolveReference(argName, ref);
    6 }


    解析RuntimeBeanReference(运行时引用)
     

     1 private Object resolveReference(Object argName, RuntimeBeanReference ref) {
     2     try {
     3         // 1、解析引用beanName
     4         Object bean;
     5         String refName = ref.getBeanName();
     6         refName = String.valueOf(doEvaluate(refName));
     7         // 2、判断引用bean是否属于父BeanFactory
     8         if (ref.isToParent()) {
     9             if (this.beanFactory.getParentBeanFactory() == null) {
    10                 throw new BeanCreationException(
    11                         this.beanDefinition.getResourceDescription(), this.beanName,
    12                         "Can't resolve reference to bean '" + refName +
    13                         "' in parent factory: no parent factory available");
    14             }
    15             bean = this.beanFactory.getParentBeanFactory().getBean(refName);
    16         }
    17         // 3、从当前beanFactory获取引用beanName实例
    18         else {
    19             bean = this.beanFactory.getBean(refName);
    20             this.beanFactory.registerDependentBean(refName, this.beanName);
    21         }
    22         if (bean instanceof NullBean) {
    23             bean = null;
    24         }
    25         return bean;
    26     }
    27     catch (BeansException ex) {
    28         throw new BeanCreationException(
    29                 this.beanDefinition.getResourceDescription(), this.beanName,
    30                 "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
    31     }
    32 }

    该过程很简单,首先解析refBeanName,然后通过getBean方法获取其实例,此时当前创建的bean是ClassA,引用bean是ClassB。

    获取到ClassB实例之后,又要填充ClassB的属性,此时又会出现对RuntimeBeanReference的解析,即ClassA,再去获取ClassA的实例,此时的ClassA的实例已经被提前曝光,会从缓存中获取ClassA的实例。

     1 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
     2     // 1、从缓存中获取bean
     3     Object singletonObject = this.singletonObjects.get(beanName);
     4     // 2、未能获取到bean,但是允许对当前创建的单例的早期引用(解决循环引用)
     5     // isSingletonCurrentlyInCreation-->判断指定的单例bean是否当前正在创建(Spring只解决单例bean的循环依赖问题)
     6     if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
     7         synchronized (this.singletonObjects) {
     8             // 从earlySingletonObjects获取提前曝光的bean
     9             singletonObject = this.earlySingletonObjects.get(beanName);
    10             // 未能获取到提前曝光的bean且当前的bean允许被创建早期依赖
    11             if (singletonObject == null && allowEarlyReference) {
    12                 // 从缓存中获取BeanFactory
    13                 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
    14                 if (singletonFactory != null) {
    15                     // 通过getObject()方法获取提前曝光的bean
    16                     singletonObject = singletonFactory.getObject();
    17                     // 将获取到的singletonObject缓存至earlySingletonObjects
    18                     this.earlySingletonObjects.put(beanName, singletonObject);
    19                     // 从singletonFactories移除bean
    20                     this.singletonFactories.remove(beanName);
    21                 }
    22             }
    23         }
    24     }
    25     return singletonObject;
    26 }

    从singletonObjects中无法获取到bean的实例,因为此时bean尚未完成全部创建,但是由于我们提前曝光了ObjectFactory,所以通过singletonObject = singletonFactory.getObject();是可以获取到bean的实例的。这样就解决了Spring的循环依赖问题。

    3.总结
    Spring只能解决Setter方法注入的单例bean之间的循环依赖
    ClassA依赖ClassB,ClassB又依赖ClassA,形成依赖闭环。Spring在获取ClassA的实例时,不等ClassA完成创建就将其曝光加入正在创建的bean缓存中。在解析ClassA的属性时,又发现依赖于ClassB,再次去获取ClassB,当解析ClassB的属性时,又发现需要ClassA的属性,但此时的ClassA已经被提前曝光加入了正在创建的bean的缓存中,则无需创建新的的ClassA的实例,直接从缓存中获取即可。从而解决循环依赖问题。
    ————————————————
    原文链接:https://blog.csdn.net/lyc_liyanchao/article/details/83099675

  • 相关阅读:
    Android之TCP服务器编程
    太阳能锂电池充电电路
    android之WIFI小车编程详述
    java 之UDP编程
    liunx目录/etc下相关配置
    五:ZooKeeper的集群命令客户端的链接和命令操作的使用
    四:ZooKeeper的集群,伪集群,单机的搭建
    三:ZooKeeper的ZAB协议
    二:ZooKeeper术语概念
    一:ZooKeeper简介
  • 原文地址:https://www.cnblogs.com/makai/p/11629196.html
Copyright © 2011-2022 走看看