亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

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

系統 1685 0

?

3.2.1? 什么是循環依賴

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

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

圖3-5 循環引用

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

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

?

java代碼:
  1. package ?cn.javass.spring.chapter3.bean;??
  2. public ? class ?CircleA?{??
  3. ???? private ?CircleB?circleB;??
  4. ???? public ?CircleA()?{??
  5. ????}??
  6. ???? public ?CircleA(CircleB?circleB)?{??
  7. ???????? this .circleB?=?circleB;??
  8. ????}??
  9. public ? void ?setCircleB(CircleB?circleB)???
  10. {??
  11. ???????? this .circleB?=?circleB;??
  12. ????}??
  13. public ? void ?a()?{??
  14. ???circleB.b();??
  15. }??
  16. }??

?

java代碼:
  1. package ?cn.javass.spring.chapter3.bean;??
  2. public ? class ?CircleB?{??
  3. ???? private ?CircleC?circleC;??
  4. ???? public ?CircleB()?{??
  5. ????}??
  6. ???? public ?CircleB(CircleC?circleC)?{??
  7. ???????? this .circleC?=?circleC;??
  8. ????}??
  9. public ? void ?setCircleC(CircleC?circleC)???
  10. {??
  11. ???????? this .circleC?=?circleC;??
  12. ????}??
  13. ???? public ? void ?b()?{??
  14. ????????circleC.c();??
  15. ????}??
  16. }??

?

java代碼:
  1. package ?cn.javass.spring.chapter3.bean;??
  2. public ? class ?CircleC?{??
  3. ???? private ?CircleA?circleA;??
  4. ???? public ?CircleC()?{??
  5. ????}??
  6. ???? public ?CircleC(CircleA?circleA)?{??
  7. ???????? this .circleA?=?circleA;??
  8. ????}??
  9. public ? void ?setCircleA(CircleA circleA)???
  10. {??
  11. ???????? this .circleA?=?circleA;??
  12. ????}??
  13. ???? public ? void ?c()?{??
  14. ????????circleA.a();??
  15. ????}??
  16. }??

?

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代碼:
  1. ??????
  2. <bean?id= "circleA" ? class = "cn.javass.spring.chapter3.bean.CircleA" >??
  3. <constructor-arg?index= "0" ?ref= "circleB" />??
  4. </bean>??
  5. <bean?id= "circleB" ? class = "cn.javass.spring.chapter3.bean.CircleB" >??
  6. <constructor-arg?index= "0" ?ref= "circleC" />??
  7. </bean>??
  8. <bean?id= "circleC" ? class = "cn.javass.spring.chapter3.bean.CircleC" >??
  9. <constructor-arg?index= "0" ?ref= "circleA" />??
  10. </bean>??
  11. ???

?

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

?

java代碼:
  1. @Test (expected?=?BeanCurrentlyInCreationException. class )??
  2. public ? void ?testCircleByConstructor()? throws ?Throwable?{??
  3. try ?{??
  4. ?????? new ?ClassPathXmlApplicationContext( "chapter3/circleInjectByConstructor.xml" );??
  5. ????}??
  6. ???? catch ?(Exception?e)?{??
  7. ?????? //因為要在創建circle3時拋出; ??
  8. ??????Throwable?e1?=?e.getCause().getCause().getCause();??
  9. ?????? throw ?e1;??
  10. ????}??
  11. }??

?

?

?????? 讓我們分析一下吧:

?????? 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代碼:
  1. addSingletonFactory(beanName,? new ?ObjectFactory()?{??
  2. ???? public ?Object?getObject()? throws ?BeansException?{??
  3. ???????? return ?getEarlyBeanReference(beanName,?mbd,?bean);??
  4. ????}??
  5. });??
  6. ???

?

?????? 具體步驟如下:

?????? 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代碼:
  1. <!--?定義Bean配置文件,注意scope都是“prototype”-->??
  2. <bean?id= "circleA" ? class = "cn.javass.spring.chapter3.bean.CircleA" ?scope= "prototype" >??
  3. ????????<property?name= "circleB" ?ref= "circleB" />??
  4. ???</bean>??
  5. ???<bean?id= "circleB" ? class = "cn.javass.spring.chapter3.bean.CircleB" ?scope= "prototype" >??
  6. ???????<property?name= "circleC" ?ref= "circleC" />??
  7. ???</bean>??
  8. ???<bean?id= "circleC" ? class = "cn.javass.spring.chapter3.bean.CircleC" ?scope= "prototype" >??
  9. ???????<property?name= "circleA" ?ref= "circleA" />??
  10. ???</bean>??

?

?

java代碼:
  1. //測試代碼cn.javass.spring.chapter3.CircleTest ??
  2. @Test (expected?=?BeanCurrentlyInCreationException. class )??
  3. public ? void ?testCircleBySetterAndPrototype?()? throws ?Throwable?{??
  4. ???? try ?{??
  5. ????????ClassPathXmlApplicationContext?ctx?=? new ?ClassPathXmlApplicationContext(??
  6. "chapter3/circleInjectBySetterAndPrototype.xml" );??
  7. ????????System.out.println(ctx.getBean( "circleA" ));??
  8. ????}??
  9. ???? catch ?(Exception?e)?{??
  10. ????????Throwable?e1?=?e.getCause().getCause().getCause();??
  11. ???????? throw ?e1;??
  12. ????}??
  13. }??

?

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

?

java代碼:
  1. @Test (expected?=?BeanCurrentlyInCreationException. class )??
  2. public ? void ?testCircleBySetterAndSingleton2()? throws ?Throwable?{??
  3. ???? try ?{??
  4. ????????ClassPathXmlApplicationContext?ctx?=??
  5. new ?ClassPathXmlApplicationContext();??
  6. ????????ctx.setConfigLocation( "chapter3/circleInjectBySetterAndSingleton.xml" );??
  7. ????????ctx.refresh();??
  8. ????}??
  9. ???? catch ?(Exception?e)?{??
  10. ????????Throwable?e1?=?e.getCause().getCause().getCause();??
  11. ???????? throw ?e1;??
  12. ????}??
  13. }??

?

補充:出現循環依賴是設計上的問題,一定要避免!

請參考《敏捷軟件開發:原則、模式與實踐》中的“無環依賴”原則

包之間的依賴結構必須是一個直接的無環圖形(DAG)。也就是說,在依賴結構中不允許出現環(循環依賴)。 ?


?

?原創內容 轉載請注明出處【 http://sishuok.com/forum/blogPost/list/0/2448.html#7070

?

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


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 久久99精品久久久久久青青日本 | 亚洲精品国产成人中文 | 九九热只有精品 | 天天视频国产免费入口 | 青青青在线视频人视频在线 | 中文字幕视频免费 | 四虎在线精品免费高清在线 | 国内久久久久影院精品 | 99最新网址 | 好爽毛片一区二区三区四区 | 夜夜爽www | 欧美日本一级在线播放 | 亚洲一区二区三区免费视频 | 中文字幕精品视频在线观 | 中文字幕一区在线观看视频 | 一区二区三区四区在线观看视频 | 中文字幕伊人 | 精品欧美一区二区在线看片 | 四虎成人欧美精品在永久在线 | 国产成人网 | 91精品全国免费观看 | 国产91福利在线精品剧情尤物 | 色女影院| 99久久精品久久久久久清纯 | 97国产视频 | 99久久99热久久 | 成人影院午夜久久影院 | 国产色婷婷精品综合在线手机播放 | 日本a级精品一区二区三区 日本a毛片在线播放 | 女人一级毛片免费观看 | 日韩精品在线视频观看 | 亚洲国产精品不卡毛片a在线 | 四虎成人精品在永久在线观看 | 香蕉国产在线观看免费 | 精品91在线 | 久久综合九色综合97婷婷群聊 | 亚洲精品视频免费在线观看 | 午夜视频在线观看网站 | chinese国产人妖视频网站 | 四虎永久免费地址在线网站 | 中文字幕久久久久久久系列 |