日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

 

1.什么是循環(huán)依賴?

當(dāng)多個(gè)Bean相互依賴時(shí)則構(gòu)成了循環(huán)依賴,例如A,B兩個(gè)Bean。其中A中存在屬性B,B中存在屬性A,當(dāng)Spring在實(shí)例化A時(shí)發(fā)現(xiàn)A中存在屬性B,就去實(shí)例化B,實(shí)例化B時(shí)又發(fā)現(xiàn)存在屬性A,一直在循環(huán)注入依賴,導(dǎo)致循環(huán)依賴問題出現(xiàn)。

2.Spring是怎么解決循環(huán)依賴的?

Spring中會(huì)通過各種Bean中間狀態(tài)來達(dá)到Bean還未實(shí)例化完成時(shí)提前將Bean提前注入到依賴Bean的屬性中,假設(shè)說Bean有三種狀態(tài)分別是青年態(tài)(一級(jí)緩存)、胚胎態(tài)(二級(jí)緩存)、小蝌蚪態(tài)(三級(jí)緩存)其中青年態(tài)代表Bean已經(jīng)實(shí)例化完成,可以直接使用了,胚胎態(tài)代表Bean已經(jīng)存在了但是還在創(chuàng)建中,還未創(chuàng)建完畢,小蝌蚪態(tài)代表還未開始創(chuàng)建,但是隨時(shí)可以進(jìn)行創(chuàng)建,三個(gè)狀態(tài)就類似于三個(gè)等級(jí),可以逐步提升從小蝌蚪狀態(tài)提升到胚胎狀態(tài)然后再提升到青年態(tài),然后Spring開始創(chuàng)建Bena時(shí)會(huì)提前將Bean存放到小蝌蚪態(tài)的緩存集合中,當(dāng)發(fā)現(xiàn)存在循環(huán)依賴時(shí)會(huì)使用存在于小蝌蚪狀態(tài)緩存集合中的Bean,提前

3.循環(huán)依賴的案例

假設(shè)例子中存在BeanA、BeanB、BeanC、BeanD四個(gè)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);

那么他們的實(shí)例化流程是什么樣的呢?

Spring源碼分析之IOC循環(huán)依賴詳解

 

發(fā)現(xiàn)了吧,Spring解決循環(huán)依賴的法寶就是緩存。

3.代碼解析(只保留相關(guān)代碼)

1.檢查緩存中是否已經(jīng)存在實(shí)例化完畢的Bean

protected Object getSingleton(String beanName, boolean allowEarlyReference) {

   //首先檢查一級(jí)緩存中是否存在
   Object singletonObject = this.singletonObjects.get(beanName);

   /**
    *  如果一級(jí)緩存中不存在代表當(dāng)前 Bean 還未被創(chuàng)建或者正在創(chuàng)建中
    *  檢查當(dāng)前 Bean 是否正處于正在創(chuàng)建的狀態(tài)中(當(dāng)Bean創(chuàng)建時(shí)會(huì)將Bean名稱存放到 singletonsCurrentlyInCreation 集合中)
   */
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      synchronized (this.singletonObjects) {
         //檢查二級(jí)緩存中是否存在
         singletonObject = this.earlySingletonObjects.get(beanName);

         /**
          * @@如果二級(jí)緩存中不存在 并且 允許使用早期依賴
          * allowEarlyReference : 它的含義是是否允許早期依賴
          * @@那么什么是早期依賴? 
          * 就是當(dāng)Bean還未成為成熟的Bean時(shí)就提前使用它,在實(shí)例化流程圖中我們看到在添加緩存前剛剛實(shí)例化Bean但是還未依賴注入時(shí)的狀態(tài)
         */
         if (singletonObject == null && allowEarlyReference) {
            
            //獲取三級(jí)緩存中的 Bean ObjectFactory
            ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
            //如果 Bean 對(duì)應(yīng)的 ObjectFactory 存在
            if (singletonFactory != null) {
               //使用 getObject 方法獲取到 Bean 的實(shí)例
               singletonObject = singletonFactory.getObject();
               //將 bean 從三級(jí)緩存提升至二級(jí)緩存
               this.earlySingletonObjects.put(beanName, singletonObject);
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   return singletonObject;
}

 

 

2.創(chuàng)建Bean

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

   /**
    * 我們一開始通過 getSingleton() 方法中獲取三級(jí)緩存中存放的Bean,這里就是向三級(jí)緩存中添加 bean 的地方
    * 流程:
    * 1.檢查當(dāng)前 bean 是否為單例模式,并且是否允許循環(huán)引用[講解1],并且當(dāng)前是否正在創(chuàng)建中(在getSingleton方法中添加的)
    * 2.如果允許提前曝光[講解2],addSingletonFactory() 方法向緩存中添加當(dāng)前 bean 的 ObjectFactory
    *
    * [講解1]:當(dāng)前 Bean 如果不允許循環(huán)引用(循環(huán)依賴也就是被依賴),則這里就不會(huì)提前曝光,對(duì)應(yīng)的 ObjectFactory
    * 則當(dāng)發(fā)生循環(huán)依賴時(shí)會(huì)拋出 BeanCreationException 異常
    *
    * [講解2]:提前曝光的含義就是說當(dāng) bean 還未創(chuàng)建完畢時(shí)就先將創(chuàng)建中狀態(tài)的bean放到指定緩存中,為循環(huán)依賴提供支持
   */
   boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
         isSingletonCurrentlyInCreation(beanName));
   //需要提前曝光
   if (earlySingletonExposure) {
      /**
       * 向緩存(三級(jí)緩存)中添加當(dāng)前 bean 的 ObjectFactory
      */
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
   }

   /**
    * Initialize the bean instance.
    * 初始化 Bean 實(shí)例階段
   */
   Object exposedObject = bean;
   try {
      /**
       *  依賴注入這時(shí)會(huì)遞歸調(diào)用getBean
      */
      populateBean(beanName, mbd, instanceWrApper);
      //調(diào)用初始化方法,如:init-method
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
   
   /**
    * 當(dāng)允許提前曝光時(shí)進(jìn)入判斷
    * @這里做了什么?
    * 1.檢查當(dāng)前bean是否經(jīng)歷了一場(chǎng)循環(huán)依賴
    *   - 通過 getSingleton(beanName,false) 獲取緩存中的 bean,傳入 false 代表不獲取三級(jí)緩存中的bean
    *   - 為什么說 檢查當(dāng)前bean是否經(jīng)歷了一場(chǎng)循環(huán)依賴呢? 因?yàn)樯鲜稣f了傳入 false 代表不獲取三級(jí)緩存的
    *   - 那么什么情況下才會(huì)存在與一級(jí)緩存和二級(jí)緩存呢?答案就是循環(huán)依賴后 [解釋1] 和bean實(shí)例化完成后
    *   - 所以如果 getSingleton 返回的 bean 不為空,則這個(gè)bean就是剛剛經(jīng)歷了循環(huán)依賴
    *
    * 2.檢查提前曝光的bean和當(dāng)前的Bean是否一致
    *   - 下面有個(gè)判斷 if (exposedObject == bean) ,這個(gè)判斷從緩存中獲取的bean 和 經(jīng)歷過初始化后的 bean
    *   - 是否一致,可能我們有點(diǎn)暈,這里解釋一下,緩存從的bean是什么時(shí)候存進(jìn)去的?是在 addSingletonFactory 方法(649行)
    *   - 然后這里存進(jìn)去的 bean 只是提前曝光的 bean,還沒有依賴注入和初始化,但是在依賴注入和初始化時(shí)都是可能直接改變
    *   - 當(dāng)前 bean 的實(shí)例的,這意味著什么?意味著經(jīng)歷了依賴注入和初始化的bean很可能和緩存中的bean就已經(jīng)完全不是一個(gè) bean了
    *   下面講解當(dāng)一致或不一致時(shí)的邏輯:
    * 2.1 一致:
    *  不是很理解,直接賦值,可是經(jīng)歷了各種 BeanPostProsser 或者依賴注入和初始化后不是就不一樣了嗎
    * 2.2 不一致:
    *  看下方對(duì)于 else if 代碼塊的解釋
    *
    * @[解釋1]
    * 當(dāng)循環(huán)依賴時(shí),A依賴著B,B依賴著A,實(shí)例化A首先將A放到三級(jí)緩存中然后發(fā)現(xiàn)依賴著B,然后去實(shí)例化B,發(fā)現(xiàn)依賴著A
    * 發(fā)現(xiàn)A在三級(jí)緩存,然后獲取三級(jí)緩存中的bean并且將A從三級(jí)緩存中提升到二級(jí)緩存中,實(shí)例化B完成,接著實(shí)例化A也完成。
    *
    * @通俗講解
    * 假設(shè)我們業(yè)務(wù)上對(duì)某種數(shù)據(jù)加了緩存,假設(shè) i 在緩存中存的值為1,當(dāng)我在數(shù)據(jù)庫中把 i 的值改成 2 時(shí),緩存中的 i 還沒有被改變還是 1
    * 這時(shí)的數(shù)據(jù)已經(jīng)和我們的真實(shí)數(shù)據(jù)偏離了,不一致了,這時(shí)有兩種解決方式:1.服務(wù)器檢查到數(shù)據(jù)不一致拋出異常。(也就是進(jìn)入else if 代碼塊)
    * 2.直接使用原始值也就是1(也就是將 allowRawInjectionDespiteWrapping 改成 true),當(dāng)然這兩種方式明顯不是我們正常數(shù)據(jù)庫的操作,只是
    * 為了說明當(dāng)前的這個(gè)例子而已。
    *
   */
   if (earlySingletonExposure) {
      //獲取緩存中(除三級(jí)緩存) beanName 對(duì)應(yīng)的 bean
      Object earlySingletonReference = getSingleton(beanName, false);
      //當(dāng)經(jīng)歷了一場(chǎng)循環(huán)依賴后 earlySingletonReference 就不會(huì)為空
      if (earlySingletonReference != null) {
         //如果 exposedObject 沒有在初始化方法中被改變,也就是沒有被增強(qiáng)
         if (exposedObject == bean) {
            //直接賦值? 可是經(jīng)歷了各種 BeanPostProsser 或者依賴注入和初始化后不是就不一樣了嗎
            exposedObject = earlySingletonReference;
         }
         /**
          *
          * 走到 else if  時(shí)說明 當(dāng)前 Bean 被 BeanPostProessor 增強(qiáng)了
          * 判斷的條件為:
          * 1.如果允許使用被增強(qiáng)的
          * 2.檢查是否存在依賴當(dāng)前bean的bean
          *
          * 如果存在依賴的bean已經(jīng)被實(shí)例化完成的,如果存在則拋出異常
          * 為什么拋出異常呢?
          * 因?yàn)橐蕾嚠?dāng)前bean 的bean 已經(jīng)在內(nèi)部注入了當(dāng)前bean的舊版本,但是通過初始化方法后這個(gè)bean的版本已經(jīng)變成新的了
          * 舊的哪個(gè)已經(jīng)不適用了,所以拋出異常
          *
         */
         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.
       * 注冊(cè) Bean 的銷毀方法拓展
      */
      registerDisposableBeanIfNecessary(beanName, bean, mbd);
   }
   catch (BeanDefinitionValidationException ex) {
      throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
   }

   return exposedObject;
}

結(jié)束

分享到:
標(biāo)簽:循環(huán) IOC
用戶無頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定