Spring IoC - 循環依賴

Spring 複習

3.循環依賴

3.1 定義

循環依賴指多個對象的建立過程當中均須要注入對方對象,以下所示java

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中將對象建立分爲以下兩步面試

  • 實例化:建立初始對象
  • 初始化:注入屬性

而且引入三級緩存,來提早暴露對象引用,從而解決循環依賴的問題spring

3.3 示例

假設A和B的建立中,field均須要對方的引用,在refresh方法進行到finishBeanFactoryInitialization(beanFactory)時,會開始建立非懶加載的singleton,這裏會先進入preInstantiateSingletons方法,根據beanName調用getBean方法,假設此時A先進行建立,那麼會進入下面方法segmentfault

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

    • getSingleton---1ide

      @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(對象未在建立過程當中),所以直接返回nullpost

      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方法this

      • 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方法中主題步驟以下.net

      • 實例化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

      • 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方法,進行初始化,初始化完成

      • 回到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也就真建立完成了

3.4 AoP的考慮

如上在有循環依賴的狀況下,假設A被代理,那麼須要將A的代理對象注入給B,這時經過getSingleton方法從三級緩存獲取對象的過程當中,因爲ObjectFactory的getObject方法被重寫爲AbstractAutowireCapableBeanFactory#getEarlyBeanReference方法,這時會觸發後置處理器的執行,會調用AbstractAutoProxyCreator的postProcessAfterInitialization方法,並返回代理對象,以後將代理對象返回用於注入,並放入二級緩存,若是A和除了B的其餘對象也構成循環依賴,以後直接從二級緩存中獲取A的代理對象便可

在沒有循環依賴的狀況下,不會使用到二級緩存,若是A被代理,那麼A會在徹底建立後,在調用後置處理器序列時,會調用AbstractAutoProxyCreator的postProcessAfterInitialization方法,並返回代理對象

從上能夠看出,

  • Spring的機制是儘可能讓代理對象靠後建立,也即在沒有循環依賴時在對象徹底建立後再建立代理對象
  • 在延遲建立代理對象的機制下,必須有二級緩存,這樣在從三級緩存中獲取時,會調用ObjectFactory方法,其又調用getEarlyBeanReference方法完成代理對象建立,以後二級緩存用於存儲代理對象,而一級緩存用於存放徹底建立完成的對象
  • Spring中,若是調用某個代理對象a的方法,其中又調用了代理對象b的方法,而不是對象b的方法

# 參考

Spring循環依賴三級緩存是否能夠減小爲二級緩存? - SegmentFault 思否

高頻面試題:Spring 如何解決循環依賴? - 知乎 (zhihu.com)

Spring-bean的循環依賴以及解決方式_惜暮-CSDN博客_spring 循環依賴

相關文章
相關標籤/搜索