什麼是循環依賴?java
循環依賴其實就是循環引用,也就是兩個或則兩個以上的bean互相持有對方,最終造成閉環。好比A依賴於B,B依賴於C,C又依賴於A。以下圖:spring
注意,這裏不是函數的循環調用,是對象的相互依賴關係。循環調用其實就是一個死循環,除非有終結條件。緩存
Spring中循環依賴場景有:性能優化
第一種:構造器參數循環依賴架構
Spring容器會將每個正在建立的Bean 標識符放在一個「當前建立Bean池」中,Bean標識符在建立過程當中將一直保持在這個池中。併發
所以若是在建立Bean過程當中發現本身已經在「當前建立Bean池」裏時將拋出BeanCurrentlyInCreationException異常表示循環依賴;而對於建立完畢的Bean將從「當前建立Bean池」中清除掉。app
首先咱們先初始化三個Bean。分佈式
OK,上面是很基本的3個類,,StudentA有參構造是StudentB。StudentB的有參構造是StudentC,StudentC的有參構造是StudentA ,這樣就產生了一個循環依賴的狀況,函數
咱們都把這三個Bean交給Spring管理,並用有參構造實例化。微服務
下面是測試類:
publicclassTest{
publicstaticvoidmain(String[] args){
ApplicationContext context =newClassPathXmlApplicationContext("com/zfx/student/applicationContext.xml");
//System.out.println(context.getBean("a", StudentA.class));
}
}
執行結果報錯信息爲:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating beanwithname'a': Requested beaniscurrentlyincreation:Isthere an unresolvable circular reference?
若是你們理解開頭那句話的話,這個報錯應該不驚訝,Spring容器先建立單例StudentA,StudentA依賴StudentB,而後將A放在「當前建立Bean池」中,此時建立StudentB,StudentB依賴StudentC ,而後將B放在「當前建立Bean池」中,此時建立StudentC,StudentC又依賴StudentA, 可是,此時Student已經在池中,因此會報錯,,由於在池中的Bean都是未初始化完的,因此會依賴錯誤 ,(初始化完的Bean會從池中移除)
第二種:setter方式單例,默認方式
若是要說setter方式注入的話,咱們最好先看一張Spring中Bean實例化的圖
如圖中前兩步驟得知:Spring是先將Bean對象實例化以後再設置對象屬性的
修改配置文件爲set方式注入
下面是測試類:
publicclassTest{
publicstaticvoidmain(String[] args){
ApplicationContext context =newClassPathXmlApplicationContext("com/zfx/student/applicationContext.xml");
System.out.println(context.getBean("a", StudentA.class));
}
}
打印結果爲:
com.zfx.student.StudentA@1fbfd6
爲何用set方式就不報錯了呢 ?
咱們結合上面那張圖看, Spring先是用構造實例化 Bean對象 ,此時Spring會將這個實例化結束的對象放到一個Map中,而且Spring提供了獲取這個未設置屬性的實例化對象引用的方法。
結合咱們的實例來看,,當Spring實例化了StudentA、StudentB、StudentC後,緊接着會去設置對象的屬性,此時StudentA依賴StudentB,就會去Map中取出存在裏面的單例StudentB對象,以此類推,不會出來循環的問題嘍、
下面是Spring源碼中的實現方法。如下的源碼在Spring的Bean包中的DefaultSingletonBeanRegistry.java類中
第三種:setter方式原型,prototype
修改配置文件爲:
測試用例:
publicclassTest{
publicstaticvoidmain(String[] args){
ApplicationContext context =newClassPathXmlApplicationContext("com/zfx/student/applicationContext.xml");
//此時必需要獲取Spring管理的實例,由於如今scope="prototype" 只有請求獲取的時候纔會實例化對象
System.out.println(context.getBean("a", StudentA.class));
}
}
打印結果:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating beanwithname'a': Requested beaniscurrentlyincreation:Isthere an unresolvable circular reference?
爲何原型模式就報錯了呢 ?
對於「prototype」做用域Bean,Spring容器沒法完成依賴注入,由於「prototype」做用域的Bean,Spring容器不進行緩存,所以沒法提早暴露一個建立中的Bean。
感謝您耐心看完的文章
順便給你們推薦一個Java技術交流羣:710373545裏面會分享一些資深架構師錄製的視頻資料:有Spring,MyBatis,Netty源碼分析,高併發、高性能、分佈式、微服務架構的原理,JVM性能優化、分佈式架構等這些成爲架構師必備的知識體系。還能領取免費的學習資源,目前受益良多!