Spring IoC - 循环依赖

Spring 复习

3.循环依赖

3.1 定义

循环依赖指多个对象的创建过程中均需要注入对方对象,如下所示

class A{
    B b;
    public A(){
    }
    public A(B b){
        this.b = b;
    }
    public void setB(B b){
        this.b = b;
    }
}
class B{
    A a;
    public B(){
    }
    public B(A a){
        this.a = a;
    }
    public void setA(A a){
        this.a = a;
    }
}

3.2 解决

Spring中将对象创建分为如下两步

  • 实例化:创建初始对象
  • 初始化:注入属性

并且引入三级缓存,来提前暴露对象引用,从而解决循环依赖的问题

3.3 示例

假设A和B的创建中,field均需要对方的引用,在refresh方法进行到finishBeanFactoryInitialization(beanFactory)时,会开始创建非懒加载的singleton,这里会先进入preInstantiateSingletons方法,根据beanName调用getBean方法,假设此时A先进行创建,那么会进入下面方法

  • doGetBeanorg.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

    • getSingleton---1

      @Override
      @Nullable
      public Object getSingleton(String beanName) {
         return getSingleton(beanName, true);
      }
      
      @Nullable
      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;
      }
      

      首先调用上面方法先从singletonObjects中场是获取,发现为null,由于isSingletonCurrentlyInCreation为false(对象未在创建过程中),因此直接返回null

      if (mbd.isSingleton()) {
         sharedInstance = getSingleton(beanName, () -> {
            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);
      }
      

      其中执行DefaultSingletonBeanRegistry#getSingleton(beanName,ObjectFactory)方法简化版如下,传入的ObjectFactory实现类是一个lambda表达式,也即用createBean方法重写ObjectFactory#getObject方法

      • getSingleton---2
      public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
         Assert.notNull(beanName, "Bean name must not be null");
         synchronized (this.singletonObjects) {
           	boolean newSingleton = false;
      
               try {
                  singletonObject = singletonFactory.getObject();
                  newSingleton = true;
               }
               catch (IllegalStateException ex) {
                  }
               }
               catch (BeanCreationException ex) {
               }
               finally {
                   
               }
               if (newSingleton) {
                  addSingleton(beanName, singletonObject);
               }
            }
            return singletonObject;
         }
      }
      

      这里第一行调用singletonFactory.getObject方法会触发createBean,又触发AbstractAutowireCapableBeanFactory#doCreateBean方法中主题步骤如下

      • 实例化bean

      • 将bean放入三级缓存singletonFactories

        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");
           }
           addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }
        

        其中调用addSingletonFactory方法如下,此处传入的lambda表达式给定的即为ObjectFactory对象,在执行其getObject方法时,即执行getEarlyBeanReference方法(这里需要留意!)

        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);
              }
           }
        }
        
      • A执行populateBean,开始注入属性b,由于B的对象还未创建,getSingleton---1(b)为null,这时触发B对象创建

      • B进行实例化

      • B放入三级缓存

      • B执行populateBean,开始注入属性a,调用getSingleton---1方法获取a,发现一级缓存singletonObject中没有对应对象,且正在创建中,则从二级缓存earlySingletonObjects中获取,发现仍然为null且allowEarlyReference默认为true,则去三级缓存中去获取,最终从三级缓存中获取,由于放入三级缓存时,lambda表达式为() -> getEarlyBeanReference(beanName, mbd, bean),所以会调用getEarlyBeanReference方法如下

        protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
           Object exposedObject = bean;
           if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
              for (BeanPostProcessor bp : getBeanPostProcessors()) {
                 if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                 }
              }
           }
           return exposedObject;
        }
        

        这里在遍历后置处理器的过程中,会调用到AbstractAutoProxyCreator的postProcessAfterInitialization方法,此方法会判断A是否被代理,如果被代理会创建代理对象并返回,之后将原有A对象从三级缓存中删除,并将A的代理对象加入到二级缓存earlySingletonObjects中,之后将A的代理对象注入给B

        创建代理对象是由AbstractAutoProxyCreator#getEarlyBeanReference完成的,如下

        public Object getEarlyBeanReference(Object bean, String beanName) {
           Object cacheKey = getCacheKey(bean.getClass(), beanName);
           this.earlyProxyReferences.put(cacheKey, bean);
           return wrapIfNecessary(bean, beanName, cacheKey);
        }
        

        其中会把原始的A对象放入earlyProxyReferences中,代表A的代理对象被提前创建并引用了

      • B执行initializeBean方法,调用后置处理器及afterProperties方法,这里提到后置处理器,同样会判断B是否被代理,如果被代理则会创建B的代理对象并返回

      • B创建结束之后,会回到getSingleton---2方法,调用addSingleton(beanName, singletonObject);方法,如下

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

        这里会将B从三级缓存中删除,并加入到一级缓存中

      • 将B创建好的对象注入到A中

      • A执行initializeBean方法,其中会调用到后置处理器的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;
        }
        

        由于在B注入A时,将A的原始对象放入了earlyProxyReferences中,因而从中获取到的bean和此处传入的是同一个bean,因而不需要再做一次额外的代理对象创建的动作,直接把A的原始对象返回即可(传给exposedObject)

      • 之后在AbstractAutowireCapableBeanFactory#doCreateBean方法内存在这样一段代码

        if (earlySingletonExposure) {
           Object earlySingletonReference = getSingleton(beanName, false);
           if (earlySingletonReference != null) {
              if (exposedObject == bean) {
                 exposedObject = earlySingletonReference;
              }
        

        其中会先从earlySingletonObjects中取出A的代理对象,由于exposedObject和bean都是A的原始对象,因而此处会将exposedObject赋值为A的代理对象!这样便替换掉了A的原始对象

        这里需要注意,生成代理对象时,其中保有原始对象的引用,也即此处的A的代理对象中引用的A对象是已经完成了属性注入的

      • 回到getSingleton--2,执行DefaultSingletonBeanRegistry#addSingleton

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

        将A的代理对象从二级缓存中删除,并加入到一级缓存中

从上面步骤可以看出

  • 三级缓存分别用于存放下面三类对象

    • 一级缓存singletonObjects

      完全创建好的对象,如果被代理,则存放代理对象

    • 二级缓存earlySingletonObjects

      (未)完全创建好的代理对象

    • 三级缓存singletonFactories

      只进行了实例化,未进行属性注入和初始化的对象

  • 为何上面机制生效

    由于提前暴露了A对象的引用!,因而在B注入好不完整的A对象后,B以为自己创建好了,这时会注入给A,同时A也会将此B对象当作创建好的,并注入给自己,这样A就创建完成了,由于B保留着A的引用,这样B也就真创建完成了

    此外,由于A先进行创建,因而其代理对象会先存放在二级缓存中,这样A在注入B之后再执行后置处理器时,就不需要再做额外的代理对象创建的操作,只需要将要放入一级缓存的对象替换为二级缓存中的代理对象即可

3.4 AoP的考虑

如上在有循环依赖的情况下,假设A被代理,那么需要将A的代理对象注入给B,这时通过getSingleton方法从三级缓存获取对象的过程中,由于ObjectFactory的getObject方法被重写为AbstractAutowireCapableBeanFactory#getEarlyBeanReference方法,这时会触发后置处理器的执行,会调用AbstractAutoProxyCreator的postProcessAfterInitialization方法,并返回代理对象,之后将代理对象返回用于注入,并放入二级缓存,如果A和除了B的其他对象也构成循环依赖,之后直接从二级缓存中获取A的代理对象即可

在没有循环依赖的情况下,不会使用到二级缓存,如果A被代理,那么A会在完全创建后,最终会将要注入到一级缓存的对象替换为之前在二级缓存中存放的代理对象

从上可以看出,

  • Spring的机制是尽量让代理对象靠后创建,也即在没有循环依赖时在对象完全创建后再创建代理对象
  • 在延迟创建代理对象的机制下,必须有二级缓存,这样在从三级缓存中获取时,会调用ObjectFactory接口的getEarlyBeanReference方法完成代理对象创建,之后二级缓存用于存储代理对象,而一级缓存用于存放完全创建完成的对象
  • Spring中,如果调用某个代理对象a的方法,其中又调用了代理对象b的方法,而不是对象b的方法

# 参考

Spring循环依赖三级缓存是否可以减少为二级缓存? - SegmentFault 思否

高频面试题:Spring 如何解决循环依赖? - 知乎 (zhihu.com)

Spring-bean的循环依赖以及解决方式_惜暮-CSDN博客_spring 循环依赖

posted @ 2021-02-27 17:10  Linus1  阅读(249)  评论(0编辑  收藏  举报