国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

【第三章】 DI 之 3.2 循環依賴 ——跟我學spring3

2019-11-08 18:50:01
字體:
來源:轉載
供稿:網友

3.2.1  什么是循環依賴

       循環依賴就是循環引用,就是兩個或多個Bean相互之間的持有對方,比如CircleA引用CircleB,CircleB引用CircleC,CircleC引用CircleA,則它們最終反映為一個環。此處不是循環調用,循環調用是方法之間的環調用。如圖3-5所示:

 

圖3-5 循環引用

       循環調用是無法解決的,除非有終結條件,否則就是死循環,最終導致內存溢出錯誤。

       SPRing容器循環依賴包括構造器循環依賴和setter循環依賴,那Spring容器如何解決循環依賴呢?首先讓我們來定義循環引用類:

 

java代碼:查看復制到剪貼板打印package cn.javass.spring.chapter3.bean;  public class CircleA {      private CircleB circleB;      public CircleA() {      }      public CircleA(CircleB circleB) {          this.circleB = circleB;      }  public void setCircleB(CircleB circleB)   {          this.circleB = circleB;      }  public void a() {     circleB.b();  }  }  

 

java代碼:查看復制到剪貼板打印package cn.javass.spring.chapter3.bean;  public class CircleB {      private CircleC circleC;      public CircleB() {      }      public CircleB(CircleC circleC) {          this.circleC = circleC;      }  public void setCircleC(CircleC circleC)   {          this.circleC = circleC;      }      public void b() {          circleC.c();      }  }  

 

java代碼:查看復制到剪貼板打印package cn.javass.spring.chapter3.bean;  public class CircleC {      private CircleC circleC;      public CircleB() {      }      public CircleB(CircleC circleC) {          this.circleC = circleC;      }  public void setCircleC(CircleC circleC)   {          this.circleC = circleC;      }      public void b() {          circleC.c();      }  }  

 

3.2.2        Spring如何解決循環依賴

一、構造器循環依賴:表示通過構造器注入構成的循環依賴,此依賴是無法解決的,只能拋出BeanCurrentlyInCreationException異常表示循環依賴。

如在創建CircleA類時,構造器需要CircleB類,那將去創建CircleB,在創建CircleB類時又發現需要CircleC類,則又去創建CircleC,最終在創建CircleC時發現又需要CircleA;從而形成一個環,沒辦法創建。

Spring容器將每一個正在創建的Bean 標識符放在一個“當前創建Bean池”中,Bean標識符在創建過程中將一直保持在這個池中,因此如果在創建Bean過程中發現自己已經在“當前創建Bean池”里時將拋出BeanCurrentlyInCreationException異常表示循環依賴;而對于創建完畢的Bean將從“當前創建Bean池”中清除掉。

       1)首先讓我們看一下配置文件(chapter3/circleInjectByConstructor.xml):

 

java代碼:查看復制到剪貼板打印      <bean id="circleA" class="cn.javass.spring.chapter3.bean.CircleA">  <constructor-arg index="0" ref="circleB"/>  </bean>  <bean id="circleB" class="cn.javass.spring.chapter3.bean.CircleB">  <constructor-arg index="0" ref="circleC"/>  </bean>  <bean id="circleC" class="cn.javass.spring.chapter3.bean.CircleC">  <constructor-arg index="0" ref="circleA"/>  </bean>     

 

       2)寫段測試代碼(cn.javass.spring.chapter3.CircleTest)測試一下吧:

 

java代碼:查看復制到剪貼板打印@Test(expected = BeanCurrentlyInCreationException.class)  public void testCircleByConstructor() throws Throwable {  try {        new ClassPathXmlapplicationContext("chapter3/circleInjectByConstructor.xml");      }      catch (Exception e) {        //因為要在創建circle3時拋出;        Throwable e1 = e.getCause().getCause().getCause();        throw e1;      }  }  

 

 

       讓我們分析一下吧:

       1、Spring容器創建“circleA” Bean,首先去“當前創建Bean池”查找是否當前Bean正在創建,如果沒發現,則繼續準備其需要的構造器參數“circleB”,并將“circleA” 標識符放到“當前創建Bean池”;

       2、Spring容器創建“circleB” Bean,首先去“當前創建Bean池”查找是否當前Bean正在創建,如果沒發現,則繼續準備其需要的構造器參數“circleC”,并將“circleB” 標識符放到“當前創建Bean池”;

3、Spring容器創建“circleC” Bean,首先去“當前創建Bean池”查找是否當前Bean正在創建,如果沒發現,則繼續準備其需要的構造器參數“circleA”,并將“circleC” 標識符放到“當前創建Bean池”;

4、到此為止Spring容器要去創建“circleA”Bean,發現該Bean 標識符在“當前創建Bean池”中,因為表示循環依賴,拋出BeanCurrentlyInCreationException。

  

二、setter循環依賴:表示通過setter注入方式構成的循環依賴。

對于setter注入造成的依賴是通過Spring容器提前暴露剛完成構造器注入但未完成其他步驟(如setter注入)的Bean來完成的,而且只能解決單例作用域的Bean循環依賴。

       如下代碼所示,通過提前暴露一個單例工廠方法,從而使其他Bean能引用到該Bean。

 

java代碼:查看復制到剪貼板打印addSingletonFactory(beanName, new ObjectFactory() {      public Object getObject() throws BeansException {          return getEarlyBeanReference(beanName, mbd, bean);      }  });     

 

       具體步驟如下:

       1、Spring容器創建單例“circleA” Bean,首先根據無參構造器創建Bean,并暴露一個“ObjectFactory ”用于返回一個提前暴露一個創建中的Bean,并將“circleA” 標識符放到“當前創建Bean池”;然后進行setter注入“circleB”;

       2、Spring容器創建單例“circleB” Bean,首先根據無參構造器創建Bean,并暴露一個“ObjectFactory”用于返回一個提前暴露一個創建中的Bean,并將“circleB” 標識符放到“當前創建Bean池”,然后進行setter注入“circleC”;

       3、Spring容器創建單例“circleC” Bean,首先根據無參構造器創建Bean,并暴露一個“ObjectFactory ”用于返回一個提前暴露一個創建中的Bean,并將“circleC” 標識符放到“當前創建Bean池”,然后進行setter注入“circleA”;進行注入“circleA”時由于提前暴露了“ObjectFactory”工廠從而使用它返回提前暴露一個創建中的Bean;

4、最后在依賴注入“circleB”和“circleA”,完成setter注入。

 

       對于“prototype”作用域Bean,Spring容器無法完成依賴注入,因為“prototype”作用域的Bean,Spring容器不進行緩存,因此無法提前暴露一個創建中的Bean。

 

java代碼:查看復制到剪貼板打印<!-- 定義Bean配置文件,注意scope都是“prototype”-->  <bean id="circleA" class="cn.javass.spring.chapter3.bean.CircleA" scope="prototype">          <property name="circleB" ref="circleB"/>     </bean>     <bean id="circleB" class="cn.javass.spring.chapter3.bean.CircleB" scope="prototype">         <property name="circleC" ref="circleC"/>     </bean>     <bean id="circleC" class="cn.javass.spring.chapter3.bean.CircleC" scope="prototype">         <property name="circleA" ref="circleA"/>     </bean>  

 

      

java代碼:查看復制到剪貼板打印//測試代碼cn.javass.spring.chapter3.CircleTest  @Test(expected = BeanCurrentlyInCreationException.class)  public void testCircleBySetterAndPrototype () throws Throwable {      try {          ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(  "chapter3/circleInjectBySetterAndPrototype.xml");          System.out.println(ctx.getBean("circleA"));      }      catch (Exception e) {          Throwable e1 = e.getCause().getCause().getCause();          throw e1;      }  }  

 

       對于“singleton”作用域Bean,可以通過“setAllowCircularReferences(false);”來禁用循環引用:

 

java代碼:查看復制到剪貼板打印@Test(expected = BeanCurrentlyInCreationException.class)  public void testCircleBySetterAndSingleton2() throws Throwable {      try {          ClassPathXmlApplicationContext ctx =  new ClassPathXmlApplicationContext();          ctx.setConfigLocation("chapter3/circleInjectBySetterAndSingleton.xml");          ctx.refresh();      }      catch (Exception e) {          Throwable e1 = e.getCause().getCause().getCause();          throw e1;      }  }  

 

    

 原創內容 #7070】


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 卫辉市| 纳雍县| 江孜县| 阿拉善左旗| 社旗县| 湖口县| 尚义县| 大埔县| 乌兰察布市| 延安市| 广德县| 长垣县| 确山县| 柘荣县| 青岛市| 江西省| 大同市| 称多县| 措勤县| 安龙县| 平潭县| 延安市| 柏乡县| 武城县| 门源| 界首市| 德昌县| 长寿区| 山西省| 弥渡县| 新宾| 内丘县| 山丹县| 渑池县| 大洼县| 蒙自县| 邳州市| 丰镇市| 白银市| 景宁| 陇南市|