什么是循環(huán)依賴?
循環(huán)依賴是指在Spring Boot 應(yīng)用程序中,兩個或多個類之間存在彼此依賴的情況,形成一個循環(huán)依賴鏈。
在這種情況下,當(dāng)一個類在初始化時需要另一個類的實(shí)例,而另一個類又需要第一個類的實(shí)例時,就會出現(xiàn)循環(huán)依賴問題。這會導(dǎo)致應(yīng)用程序無法正確地初始化和運(yùn)行,因?yàn)镾pring Boot 無法處理這種循環(huán)依賴關(guān)系。
問題及癥狀
在2.6.0之前,Spring Boot會自動處理循環(huán)依賴的問題。2.6.0及之后的版本會默認(rèn)檢查循環(huán)依賴,存在該問題則會報(bào)錯。
ComponentA類注入ComponentB類,ComponentB類注入ComponentA類,就會發(fā)生循環(huá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; }
錯誤
現(xiàn)在,2.6.0 這個版本已經(jīng)默認(rèn)禁止 Bean 之間的循環(huán)引用, 則基于上面的代碼,會報(bào)錯:
*************************** APPLICATIONFAILEDTOSTART *************************** Description: Thedependenciesofsomeofthebeansintheapplicationcontextformacycle: ┌─────┐ |componentA ↑↓ |componentB └─────┘ Action: Relyinguponcircularreferencesisdiscouragedandtheyareprohibitedbydefault.Updateyourapplicationtoremovethedependencycyclebetweenbeans.Asalastresort,itmaybepossibletobreakthecycleautomaticallybysettingspring.main.allow-circular-referencestotrue.
解決方法
循環(huán)依賴是指兩個或更多的組件之間存在著互相依賴的關(guān)系。在Spring Boot應(yīng)用程序中,循環(huán)依賴通常是由以下幾種情況引起的:
構(gòu)造函數(shù)循環(huán)依賴: 兩個或更多的組件在它們的構(gòu)造函數(shù)中互相依賴。
屬性循環(huán)依賴: 兩個或更多的組件在它們的屬性中互相依賴。
方法循環(huán)依賴: 兩個或更多的組件在它們的方法中互相依賴。
Spring Boot提供了一些解決循環(huán)依賴的方法:
構(gòu)造函數(shù)注入: 在構(gòu)造函數(shù)中注入依賴項(xiàng),而不是在屬性中注入。
Setter注入: 使用setter方法注入依賴項(xiàng),而不是在構(gòu)造函數(shù)中注入。
延遲注入: 使用@Lazy注解延遲加載依賴項(xiàng)。
@Autowired注解的required屬性: 將required屬性設(shè)置為false,以避免出現(xiàn)循環(huán)依賴問題。
@DependsOn注解: 使用@DependsOn注解指定依賴項(xiàng)的加載順序,以避免出現(xiàn)循環(huán)依賴問題
構(gòu)造器注入的案例
假設(shè)有以下兩個類:
publicclassA{ privateBb; publicA(){ //... } publicvoidsetB(Bb){ this.b=b; } } publicclassB{ privateAa; publicB(){ //... } publicvoidsetA(Aa){ this.a=a; } }
通過構(gòu)造函數(shù)注入可以避免循環(huán)依賴,改造后的代碼如下:
publicclassA{ privateBb; publicA(Bb){ this.b=b; } } publicclassB{ privateAa; publicB(Aa){ this.a=a; } }
這樣,在創(chuàng)建 A 實(shí)例時,只需要將 B 實(shí)例傳遞給 A 的構(gòu)造函數(shù)即可,不需要再通過 setter 方法將 B 實(shí)例注入到 A 中。同理,在創(chuàng)建 B 實(shí)例時,只需要將 A 實(shí)例傳遞給 B 的構(gòu)造函數(shù)即可,不需要再通過 setter 方法將 A 實(shí)例注入到 B 中。這樣可以避免循環(huán)依賴。
延遲注入的案例
假設(shè)有如下情景:
類A依賴于類B,同時類B也依賴于類A。這樣就形成了循環(huán)依賴。
為了解決這個問題,可以使用@Lazy注解,將類A或類B中的其中一個延遲加載。
例如,我們可以在類A中使用@Lazy注解,將類A延遲加載,這樣在啟動應(yīng)用程序時,Spring容器不會立即加載類A,而是在需要使用類A的時候才會進(jìn)行加載。這樣就避免了循環(huán)依賴的問題。
示例代碼如下:
@Component publicclassA{ privatefinalBb; publicA(@LazyBb){ this.b=b; } //... } @Component publicclassB{ privatefinalAa; publicB(Aa){ this.a=a; } //... }
在類A中,我們使用了@Lazy注解,將類B延遲加載。這樣在啟動應(yīng)用程序時,Spring容器不會立即加載類B,而是在需要使用類B的時候才會進(jìn)行加載。
這樣就避免了類A和類B之間的循環(huán)依賴問題。
接口隔離的案例
假設(shè)有兩個類A和B,它們之間存在循環(huán)依賴:
publicclassA{ privatefinalBb; publicA(Bb){ this.b=b; } } publicclassB{ privatefinalAa; publicB(Aa){ this.a=a; } }
這時候,如果直接在Spring Boot中注入A和B,就會出現(xiàn)循環(huán)依賴的問題。為了解決這個問題,可以使用接口隔離。
首先,定義一個接口,包含A和B類中需要使用的方法:
publicinterfaceService{ voiddoSomething(); }
然后,在A和B類中分別注入Service接口:
publicclassA{ privatefinalServiceservice; publicA(Serviceservice){ this.service=service; } } publicclassB{ privatefinalServiceservice; publicB(Serviceservice){ this.service=service; } }
最后,在Spring Boot中注入Service實(shí)現(xiàn)類:
@Service publicclassServiceImplimplementsService{ privatefinalAa; privatefinalBb; publicServiceImpl(Aa,Bb){ this.a=a; this.b=b; } @Override publicvoiddoSomething(){ //... } }
通過這種方式,A和B類不再直接依賴于彼此,而是依賴于同一個接口。同時,Spring Boot也能夠正確地注入A、B和ServiceImpl,避免了循環(huán)依賴的問題。
審核編輯:湯梓紅
-
接口
+關(guān)注
關(guān)注
33文章
8447瀏覽量
150720 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4277瀏覽量
62323 -
應(yīng)用程序
+關(guān)注
關(guān)注
37文章
3237瀏覽量
57547 -
spring
+關(guān)注
關(guān)注
0文章
338瀏覽量
14295 -
Boot
+關(guān)注
關(guān)注
0文章
149瀏覽量
35750 -
SpringBoot
+關(guān)注
關(guān)注
0文章
173瀏覽量
161
原文標(biāo)題:SpringBoot 循環(huán)依賴的癥狀和解決方案
文章出處:【微信號:AndroidPush,微信公眾號:Android編程精選】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論