Spring原始碼深度解析之迴圈依賴

2021-12-29 21:38:05 字數 3346 閱讀 9304

如果大家看過spring例項化bean的核心方法——docreatebean,大家會發現例項化bean是乙個非常複雜的過程,而這其中最難以理解的就是對迴圈依賴的解決,這裡我們就對此點問題進行一下說明。

什麼是迴圈依賴

首先我們要弄懂迴圈依賴是什麼。迴圈依賴就是迴圈引用,就是兩個或者多個bean相互之間持有對方,比如circlea、circleb、circlec三個類之間circlea引用circleb、circleb引用circlec、circlec引用circlea,則它們最終反映為乙個環。

注意:迴圈引用和迴圈呼叫的區別,迴圈引用是屬性之間的環引用,而迴圈呼叫是方法之間環呼叫,迴圈呼叫是無法解決的,除非有終結條件,否則就是死迴圈,最終導致記憶體溢位錯誤。

spring如何解決迴圈依賴

spring容器迴圈依賴構造器迴圈依賴和setter迴圈依賴,那spring容器如何解決迴圈依賴呢,首先我們來定義迴圈引用類:

public class testa

public testb gettestb()

public void settestb(testb testb)

}public class testb

public testc gettestc()

public void settestc(testc testc)

}public class testc

public testa gettesta()

public void settesta(testa testa)

}在spring中將迴圈依賴的處理分成了3種情況。

1、構造器迴圈依賴

表示通過構造器注入構成的迴圈依賴,此依賴是無法解決的,只能丟擲beancurrentlyincreationexception異常表示迴圈依賴。如在建立testa類時,構造器需要testb類,那將去建立testb,在建立testb類時又發現需要testc類,則又去建立testc類,最終在建立testc類時候發現又需要testa,從而形成乙個環,沒辦法建立。

spring容器將每乙個正在建立的bean識別符號放在乙個「當前建立bean池中」,bean識別符號在建立過程中將一直保持在這個池中,因此如果在建立bean過程中發現自己已經在「當前建立bean池」裡時,則丟擲beancurrentlyincreationexception異常表示迴圈依賴;而對於建立完畢的bean將從「當前建立bean池」中清除掉。

下面我們通過乙個直觀的測試用例來進行分析。

(1)建立配置檔案

(2)建立測試用例

@test(expected = beancurrentlyincreationexception.class)

public void testcirclebyconstructor() throwablecatch(exception e)

}針對以上**的分析如下:

i.spring容器建立「testa」bean,首先去「當前建立bean池」查詢是否當前bean正在建立,如果沒發現,則繼續準備其需要的構造器引數「testb」,並將「testa」識別符號放到「當前建立bean池」中

ii.spring容器建立「testb」bean,首先去「當前建立bean池」查詢是否當前bean正在建立,如果沒發現,則繼續準備其需要的構造器引數「testc」,並將「testb」識別符號放到「當前建立bean池」中

iii.spring容器建立「testc」bean,首先去「當前建立bean池」查詢是否當前bean正在建立,如果沒發現,則繼續準備其需要的構造器引數「testa」,並將「testc」識別符號放到「當前建立bean池」中

iv.到此為止spring容器要去建立「testa」bean,發現該bean識別符號在「當前建立bean池」中,因此標識迴圈依賴,丟擲beancurrentlyincreationexception。

2、setter迴圈依賴

表示通過setter注入方式構成的迴圈依賴。對應setter注入造成的依賴是通過spring容器提前暴露剛完成構造器注入但未完成其他步驟(如setter注入)的bean來完成的,而且只能解決單例作用域的bean迴圈依賴。通過提前暴露乙個單例工廠方法,從而使其他bean能引用到該bean,如下**所示

addsingletonfactory(beanname, () -> getearlybeanreference(beanname, mbd, bean));具體步驟如下。

i.spring容器建立單例「testa」bean,首先根據無引數構造器建立bean,並暴露乙個「objectfactory」用於返回乙個提前暴露的建立中的bean,並將「testa」識別符號放到「當前建立bean池」,然後進行setter注入「testb」。

ii.spring容器建立單例「testb」bean,首先根據無引數構造器建立bean,並暴露乙個「objectfactory」用於返回乙個提前暴露的建立中的bean,並將「testb」識別符號放到「當前建立bean池」,然後進行setter注入「testc」。

iii.spring容器建立單例「testc」bean,首先根據無引數構造器建立bean,並暴露乙個「objectfactory」用於返回乙個提前暴露的建立中的bean,並將「testb」識別符號放到「當前建立bean池」,然後進行setter注入「testa」。進行注入「testa」時由於提前暴露了「objectfactory」工廠,從而使用它返回提前暴露乙個建立中的bean。

iv.最後依賴注入「testb」和「testa」,完成setter注入。

3、prototype範圍的依賴處理

對於「prototype」作用域bean,spring容器無法完成依賴注入,因為spring容器不進行快取「prototype」作用域的bean,因此無法提前暴露乙個建立中的bean,示例如下:

(1)建立配置檔案

(2)建立測試用例

@test(expected = beancurrentlyincreationexception.class)

public void testcirclebysetterandprototype() throws throwablecatch(exception e)

}對於「singleton」作用域bean,可以通過setallowcirclereferences(false)來禁用迴圈引用。

Spring原始碼解析之 Aop原始碼解析(2)

spring aop 更多的是oop開發模式的乙個補充,幫助oop以更好的方式來解決對於需要解決業務功能模組之上統一管理 的功能 以一副圖來做為aop功能的說明更直觀些。對於類似系統的安全檢查,系統日誌,事務管理等相關功能,物件導向的開發方法並沒有更好的解決方法 aop引入了一些概念。更多的是spr...

Spring原始碼之XML解析

資料準備階段 準備的目的是封裝 resource引數,目的是為了考慮到 resource可能存在編碼要求的情況,其次,通過 sax讀取 xml檔案的方式來準備 inputsource物件,最後將引數傳遞到 最核心的實現部分 doloadbeandefinitions inputsource,enco...

Spring 原始碼解析之BeanFactory介面

beanfactory介面是spring 容器的根介面,其他介面和類通過對這個介面的實現物件的例項化,通過對該介面的控制,實現對目標物件的例項化。string factory bean prefix 用於取消對beanfactory例項的引用區分factory和其實例,如果是 返回factory,否...