Spring源码分析之IOC循环依赖

1.什么是循环依赖?

当多个Bean相互依赖时则构成了循环依赖,例如A,B两个Bean。其中A中存在属性B,B中存在属性A,当Spring在实例化A时发现A中存在属性B,就去实例化B,实例化B时又发现存在属性A,一直在循环注入依赖,致使循环依赖问题出现。

2.Spring是怎么解决循环依赖的?

Spring中会经过各类Bean中间状态来达到Bean还未实例化完成时提早将Bean提早注入到依赖Bean的属性中,假设说Bean有三种状态分别是青年态(一级缓存)、胚胎态(二级缓存)、小蝌蚪态(三级缓存)其中青年态表明Bean已经实例化完成,能够直接使用了,胚胎态表明Bean已经存在了可是还在建立中,还未建立完毕,小蝌蚪态表明还未开始建立,可是随时能够进行建立,三个状态就相似于三个等级,能够逐步提高从小蝌蚪状态提高到胚胎状态而后再提高到青年态,而后Spring开始建立Bena时会提早将Bean存放到小蝌蚪态的缓存集合中,当发现存在循环依赖时会使用存在于小蝌蚪状态缓存集合中的Bean,提早

3.循环依赖的案例

假设例子中存在BeanA、BeanB、BeanC、BeanD四个Bean,其中
  • BeanA依赖着BeanB,C
  • BeanB依赖着BeanC
  • BeanC依赖着BeanA
  • BeanD依赖着BeanA,B,C
BeanA beanA = beanFactory.getBean("beanA",BeanA.class); BeanB beanB = beanFactory.getBean("beanB",BeanB.class); BeanC beanC = beanFactory.getBean("beanC",BeanC.class); BeanD beanD = beanFactory.getBean("beanD",BeanD.class);
那么他们的实例化流程是什么样的呢?
发现了吧,Spring解决循环依赖的法宝就是缓存。

3.代码解析(只保留相关代码)

1.检查缓存中是否已经存在实例化完毕的Bean

protected Object getSingleton(String beanName, boolean allowEarlyReference) {

   //首先检查一级缓存中是否存在
   Object singletonObject = this.singletonObjects.get(beanName);

   /** * 若是一级缓存中不存在表明当前 Bean 还未被建立或者正在建立中 * 检查当前 Bean 是否正处于正在建立的状态中(当Bean建立时会将Bean名称存放到 singletonsCurrentlyInCreation 集合中) */
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      synchronized (this.singletonObjects) {
         //检查二级缓存中是否存在
         singletonObject = this.earlySingletonObjects.get(beanName);

         /** * @@若是二级缓存中不存在 而且 容许使用早期依赖 * allowEarlyReference : 它的含义是是否容许早期依赖 * @@那么什么是早期依赖? * 就是当Bean还未成为成熟的Bean时就提早使用它,在实例化流程图中咱们看到在添加缓存前刚刚实例化Bean可是还未依赖注入时的状态 */
         if (singletonObject == null && allowEarlyReference) {
            
            //获取三级缓存中的 Bean ObjectFactory
            ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
            //若是 Bean 对应的 ObjectFactory 存在
            if (singletonFactory != null) {
               //使用 getObject 方法获取到 Bean 的实例
               singletonObject = singletonFactory.getObject();
               //将 bean 从三级缓存提高至二级缓存
               this.earlySingletonObjects.put(beanName, singletonObject);
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   return singletonObject;
}复制代码

2.建立Bean

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {

   /** * 咱们一开始经过 getSingleton() 方法中获取三级缓存中存放的Bean,这里就是向三级缓存中添加 bean 的地方 * 流程: * 1.检查当前 bean 是否为单例模式,而且是否容许循环引用[讲解1],而且当前是否正在建立中(在getSingleton方法中添加的) * 2.若是容许提早曝光[讲解2],addSingletonFactory() 方法向缓存中添加当前 bean 的 ObjectFactory * * [讲解1]:当前 Bean 若是不容许循环引用(循环依赖也就是被依赖),则这里就不会提早曝光,对应的 ObjectFactory * 则当发生循环依赖时会抛出 BeanCreationException 异常 * * [讲解2]:提早曝光的含义就是说当 bean 还未建立完毕时就先将建立中状态的bean放到指定缓存中,为循环依赖提供支持 */
   boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
         isSingletonCurrentlyInCreation(beanName));
   //须要提早曝光
   if (earlySingletonExposure) {
      /** * 向缓存(三级缓存)中添加当前 bean 的 ObjectFactory */
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
   }

   /** * Initialize the bean instance. * 初始化 Bean 实例阶段 */
   Object exposedObject = bean;
   try {
      /** * 依赖注入这时会递归调用getBean */
      populateBean(beanName, mbd, instanceWrapper);
      //调用初始化方法,如:init-method
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
   
   /** * 当容许提早曝光时进入判断 * @这里作了什么? * 1.检查当前bean是否经历了一场循环依赖 * - 经过 getSingleton(beanName,false) 获取缓存中的 bean,传入 false 表明不获取三级缓存中的bean * - 为何说 检查当前bean是否经历了一场循环依赖呢? 由于上述说了传入 false 表明不获取三级缓存的 * - 那么什么状况下才会存在与一级缓存和二级缓存呢?答案就是循环依赖后 [解释1] 和bean实例化完成后 * - 因此若是 getSingleton 返回的 bean 不为空,则这个bean就是刚刚经历了循环依赖 * * 2.检查提早曝光的bean和当前的Bean是否一致 * - 下面有个判断 if (exposedObject == bean) ,这个判断从缓存中获取的bean 和 经历过初始化后的 bean * - 是否一致,可能咱们有点晕,这里解释一下,缓存从的bean是何时存进去的?是在 addSingletonFactory 方法(649行) * - 而后这里存进去的 bean 只是提早曝光的 bean,尚未依赖注入和初始化,可是在依赖注入和初始化时都是可能直接改变 * - 当前 bean 的实例的,这意味着什么?意味着经历了依赖注入和初始化的bean极可能和缓存中的bean就已经彻底不是一个 bean了 * 下面讲解当一致或不一致时的逻辑: * 2.1 一致: * 不是很理解,直接赋值,但是经历了各类 BeanPostProsser 或者依赖注入和初始化后不是就不同了吗 * 2.2 不一致: * 看下方对于 else if 代码块的解释 * * @[解释1] * 当循环依赖时,A依赖着B,B依赖着A,实例化A首先将A放到三级缓存中而后发现依赖着B,而后去实例化B,发现依赖着A * 发现A在三级缓存,而后获取三级缓存中的bean而且将A从三级缓存中提高到二级缓存中,实例化B完成,接着实例化A也完成。 * * @通俗讲解 * 假设咱们业务上对某种数据加了缓存,假设 i 在缓存中存的值为1,当我在数据库中把 i 的值改为 2 时,缓存中的 i 尚未被改变仍是 1 * 这时的数据已经和咱们的真实数据偏离了,不一致了,这时有两种解决方式:1.服务器检查到数据不一致抛出异常。(也就是进入else if 代码块) * 2.直接使用原始值也就是1(也就是将 allowRawInjectionDespiteWrapping 改为 true),固然这两种方式明显不是咱们正常数据库的操做,只是 * 为了说明当前的这个例子而已。 * */
   if (earlySingletonExposure) {
      //获取缓存中(除三级缓存) beanName 对应的 bean
      Object earlySingletonReference = getSingleton(beanName, false);
      //当经历了一场循环依赖后 earlySingletonReference 就不会为空
      if (earlySingletonReference != null) {
         //若是 exposedObject 没有在初始化方法中被改变,也就是没有被加强
         if (exposedObject == bean) {
            //直接赋值? 但是经历了各类 BeanPostProsser 或者依赖注入和初始化后不是就不同了吗
            exposedObject = earlySingletonReference;
         }
         /** * * 走到 else if 时说明 当前 Bean 被 BeanPostProessor 加强了 * 判断的条件为: * 1.若是容许使用被加强的 * 2.检查是否存在依赖当前bean的bean * * 若是存在依赖的bean已经被实例化完成的,若是存在则抛出异常 * 为何抛出异常呢? * 由于依赖当前bean 的bean 已经在内部注入了当前bean的旧版本,可是经过初始化方法后这个bean的版本已经变成新的了 * 旧的哪一个已经不适用了,因此抛出异常 * */
         else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
            String[] dependentBeans = getDependentBeans(beanName);
            Set 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 " +
                     "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
            }
         }
      }
   }

   try {
      /** * Register bean as disposable. * 注册 Bean 的销毁方法拓展 */
      registerDisposableBeanIfNecessary(beanName, bean, mbd);
   }
   catch (BeanDefinitionValidationException ex) {
      throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
   }

   return exposedObject;
}复制代码

结束语

我也在学习过程当中,若是发现文中有不对的地方请直接联系捶我就好,若是有兴趣能够联系我咱们一块儿学习!java

相关文章
相关标签/搜索