什么是循環(huán)依賴(lài)?
循環(huán)依賴(lài)是指在Spring Boot 應(yīng)用程序中,兩個(gè)或多個(gè)類(lèi)之間存在彼此依賴(lài)的情況,形成一個(gè)循環(huán)依賴(lài)鏈。
在這種情況下,當(dāng)一個(gè)類(lèi)在初始化時(shí)需要另一個(gè)類(lèi)的實(shí)例,而另一個(gè)類(lèi)又需要第一個(gè)類(lèi)的實(shí)例時(shí),就會(huì)出現(xiàn)循環(huán)依賴(lài)問(wèn)題。這會(huì)導(dǎo)致應(yīng)用程序無(wú)法正確地初始化和運(yùn)行,因?yàn)镾pring Boot 無(wú)法處理這種循環(huán)依賴(lài)關(guān)系。
問(wèn)題及癥狀
在2.6.0之前,Spring Boot會(huì)自動(dòng)處理循環(huán)依賴(lài)的問(wèn)題。2.6.0及之后的版本會(huì)默認(rèn)檢查循環(huán)依賴(lài),存在該問(wèn)題則會(huì)報(bào)錯(cuò)。
ComponentA類(lèi)注入ComponentB類(lèi),ComponentB類(lèi)注入ComponentA類(lèi),就會(huì)發(fā)生循環(huán)依賴(lài)的問(wèn)題。
ComponentA
importorg.springframework.stereotype.Service; importjavax.annotation.Resource; @Service publicclassComponentA{ @Resource privateComponentBcomponentB; }
ComponentB
importorg.springframework.stereotype.Service;
importjavax.annotation.Resource;
@Service
publicclassComponentB{
@Resource
privateComponentAcomponentA;
}
錯(cuò)誤
現(xiàn)在,2.6.0 這個(gè)版本已經(jīng)默認(rèn)禁止 Bean 之間的循環(huán)引用, 則基于上面的代碼,會(huì)報(bào)錯(cuò):
*************************** APPLICATIONFAILEDTOSTART *************************** Description: Thedependenciesofsomeofthebeansintheapplicationcontextformacycle: ┌─────┐ |componentA ↑↓ |componentB └─────┘ Action: Relyinguponcircularreferencesisdiscouragedandtheyareprohibitedbydefault.Updateyourapplicationtoremovethedependencycyclebetweenbeans.Asalastresort,itmaybepossibletobreakthecycleautomaticallybysettingspring.main.allow-circular-referencestotrue.
解決方法
循環(huán)依賴(lài)是指兩個(gè)或更多的組件之間存在著互相依賴(lài)的關(guān)系。在Spring Boot應(yīng)用程序中,循環(huán)依賴(lài)通常是由以下幾種情況引起的:
構(gòu)造函數(shù)循環(huán)依賴(lài): 兩個(gè)或更多的組件在它們的構(gòu)造函數(shù)中互相依賴(lài)。
屬性循環(huán)依賴(lài): 兩個(gè)或更多的組件在它們的屬性中互相依賴(lài)。
方法循環(huán)依賴(lài): 兩個(gè)或更多的組件在它們的方法中互相依賴(lài)。
Spring Boot提供了一些解決循環(huán)依賴(lài)的方法:
構(gòu)造函數(shù)注入: 在構(gòu)造函數(shù)中注入依賴(lài)項(xiàng),而不是在屬性中注入。
Setter注入: 使用setter方法注入依賴(lài)項(xiàng),而不是在構(gòu)造函數(shù)中注入。
延遲注入: 使用@Lazy注解延遲加載依賴(lài)項(xiàng)。
@Autowired注解的required屬性: 將required屬性設(shè)置為false,以避免出現(xiàn)循環(huán)依賴(lài)問(wèn)題。
@DependsOn注解: 使用@DependsOn注解指定依賴(lài)項(xiàng)的加載順序,以避免出現(xiàn)循環(huán)依賴(lài)問(wèn)題
構(gòu)造器注入的案例
假設(shè)有以下兩個(gè)類(lèi):
publicclassA{
privateBb;
publicA(){
//...
}
publicvoidsetB(Bb){
this.b=b;
}
}
publicclassB{
privateAa;
publicB(){
//...
}
publicvoidsetA(Aa){
this.a=a;
}
}
通過(guò)構(gòu)造函數(shù)注入可以避免循環(huán)依賴(lài),改造后的代碼如下:
publicclassA{
privateBb;
publicA(Bb){
this.b=b;
}
}
publicclassB{
privateAa;
publicB(Aa){
this.a=a;
}
}
這樣,在創(chuàng)建 A 實(shí)例時(shí),只需要將 B 實(shí)例傳遞給 A 的構(gòu)造函數(shù)即可,不需要再通過(guò) setter 方法將 B 實(shí)例注入到 A 中。同理,在創(chuàng)建 B 實(shí)例時(shí),只需要將 A 實(shí)例傳遞給 B 的構(gòu)造函數(shù)即可,不需要再通過(guò) setter 方法將 A 實(shí)例注入到 B 中。這樣可以避免循環(huán)依賴(lài)。
延遲注入的案例
假設(shè)有如下情景:
類(lèi)A依賴(lài)于類(lèi)B,同時(shí)類(lèi)B也依賴(lài)于類(lèi)A。這樣就形成了循環(huán)依賴(lài)。
為了解決這個(gè)問(wèn)題,可以使用@Lazy注解,將類(lèi)A或類(lèi)B中的其中一個(gè)延遲加載。
例如,我們可以在類(lèi)A中使用@Lazy注解,將類(lèi)A延遲加載,這樣在啟動(dòng)應(yīng)用程序時(shí),Spring容器不會(huì)立即加載類(lèi)A,而是在需要使用類(lèi)A的時(shí)候才會(huì)進(jìn)行加載。這樣就避免了循環(huán)依賴(lài)的問(wèn)題。
示例代碼如下:
@Component
publicclassA{
privatefinalBb;
publicA(@LazyBb){
this.b=b;
}
//...
}
@Component
publicclassB{
privatefinalAa;
publicB(Aa){
this.a=a;
}
//...
}
在類(lèi)A中,我們使用了@Lazy注解,將類(lèi)B延遲加載。這樣在啟動(dòng)應(yīng)用程序時(shí),Spring容器不會(huì)立即加載類(lèi)B,而是在需要使用類(lèi)B的時(shí)候才會(huì)進(jìn)行加載。
這樣就避免了類(lèi)A和類(lèi)B之間的循環(huán)依賴(lài)問(wèn)題。
接口隔離的案例
假設(shè)有兩個(gè)類(lèi)A和B,它們之間存在循環(huán)依賴(lài):
publicclassA{
privatefinalBb;
publicA(Bb){
this.b=b;
}
}
publicclassB{
privatefinalAa;
publicB(Aa){
this.a=a;
}
}
這時(shí)候,如果直接在Spring Boot中注入A和B,就會(huì)出現(xiàn)循環(huán)依賴(lài)的問(wèn)題。為了解決這個(gè)問(wèn)題,可以使用接口隔離。
首先,定義一個(gè)接口,包含A和B類(lèi)中需要使用的方法:
publicinterfaceService{
voiddoSomething();
}
然后,在A和B類(lèi)中分別注入Service接口:
publicclassA{
privatefinalServiceservice;
publicA(Serviceservice){
this.service=service;
}
}
publicclassB{
privatefinalServiceservice;
publicB(Serviceservice){
this.service=service;
}
}
最后,在Spring Boot中注入Service實(shí)現(xiàn)類(lèi):
@Service publicclassServiceImplimplementsService{ privatefinalAa; privatefinalBb; publicServiceImpl(Aa,Bb){ this.a=a; this.b=b; } @Override publicvoiddoSomething(){ //... } }
通過(guò)這種方式,A和B類(lèi)不再直接依賴(lài)于彼此,而是依賴(lài)于同一個(gè)接口。同時(shí),Spring Boot也能夠正確地注入A、B和ServiceImpl,避免了循環(huán)依賴(lài)的問(wèn)題。
審核編輯:湯梓紅
-
接口
+關(guān)注
關(guān)注
33文章
9587瀏覽量
157583 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4421瀏覽量
67815 -
應(yīng)用程序
+關(guān)注
關(guān)注
38文章
3346瀏覽量
60404 -
spring
+關(guān)注
關(guān)注
0文章
341瀏覽量
16040 -
Boot
+關(guān)注
關(guān)注
0文章
154瀏覽量
37904 -
SpringBoot
+關(guān)注
關(guān)注
0文章
178瀏覽量
709
原文標(biāo)題:SpringBoot 循環(huán)依賴(lài)的癥狀和解決方案
文章出處:【微信號(hào):AndroidPush,微信公眾號(hào):Android編程精選】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
2009最新LED路燈技術(shù)設(shè)計(jì)和解決方案
儀表放大器--常見(jiàn)的應(yīng)用問(wèn)題和解決方案
EMI耦合類(lèi)型和解決方案?
SpringBoot 學(xué)習(xí)筆記
springboot集成mqtt
AN-1429: 手機(jī)中耳機(jī)驅(qū)動(dòng)的設(shè)計(jì)考慮和解決方案
SpringBoot循環(huán)依賴(lài)的癥狀和解決方案
評(píng)論