基于SpringBoot構(gòu)造器注入循環(huán)依賴及解決方式
1. 循環(huán)依賴是什么?
Bean A 依賴 B,Bean B 依賴 A這種情況下出現(xiàn)循環(huán)依賴。
Bean A → Bean B → Bean A
更復(fù)雜的間接依賴造成的循環(huán)依賴如下。
Bean A → Bean B → Bean C → Bean D → Bean E → Bean A
2. 循環(huán)依賴會產(chǎn)生什么結(jié)果?
當(dāng)Spring正在加載所有Bean時,Spring嘗試以能正常創(chuàng)建Bean的順序去創(chuàng)建Bean。
例如,有如下依賴:
Bean A → Bean B → Bean C
Spring先創(chuàng)建beanC,接著創(chuàng)建bean B(將C注入B中),最后創(chuàng)建bean A(將B注入A中)。
但當(dāng)存在循環(huán)依賴時,Spring將無法決定先創(chuàng)建哪個bean。這種情況下,Spring將產(chǎn)生異常BeanCurrentlyInCreationException。
當(dāng)使用構(gòu)造器注入時經(jīng)常會發(fā)生循環(huán)依賴問題。如果使用其它類型的注入方式能夠避免這種問題。
3. 構(gòu)造器注入循環(huán)依賴實例
首先定義兩個相互通過構(gòu)造器注入依賴的bean。
@Component public class CircularDependencyA { private CircularDependencyB circB; @Autowired public CircularDependencyA(CircularDependencyB circB) { this.circB = circB; } }
@Component public class CircularDependencyB { private CircularDependencyA circA; @Autowired public CircularDependencyB(CircularDependencyA circA) { this.circA = circA; } }
@Configuration @ComponentScan(basePackages = { "com.baeldung.circulardependency" }) public class TestConfig { }
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { TestConfig.class }) public class CircularDependencyTest { @Test public void givenCircularDependency_whenConstructorInjection_thenItFails() { // Empty test; we just want the context to load } }
運行方法givenCircularDependency_whenConstructorInjection_thenItFails將會產(chǎn)生異常:
BeanCurrentlyInCreationException: Error creating bean with name ‘circularDependencyA': Requested bean is currently in creation: Is there an unresolvable circular reference?
4.解決方法
處理這種問題目前有如下幾種常見方式。
4.1 重新設(shè)計
重新設(shè)計結(jié)構(gòu),消除循環(huán)依賴。
4.2 使用注解 @Lazy
一種最簡單的消除循環(huán)依賴的方式是通過延遲加載。在注入依賴時,先注入代理對象,當(dāng)首次使用時再創(chuàng)建對象完成注入。
@Component public class CircularDependencyA { private CircularDependencyB circB; @Autowired public CircularDependencyA(@Lazy CircularDependencyB circB) { this.circB = circB; } }
使用@Lazy后,運行代碼,可以看到異常消除。
4.3 使用Setter/Field注入
Spring文檔建議的一種方式是使用setter注入。當(dāng)依賴最終被使用時才進行注入。對前文的樣例代碼少做修改,來觀察測試效果。
@Component public class CircularDependencyA { private CircularDependencyB circB; @Autowired public void setCircB(CircularDependencyB circB) { this.circB = circB; } public CircularDependencyB getCircB() { return circB; } }
@Component public class CircularDependencyB { private CircularDependencyA circA; private String message = "Hi!"; @Autowired public void setCircA(CircularDependencyA circA) { this.circA = circA; } public String getMessage() { return message; } }
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { TestConfig.class }) public class CircularDependencyTest { @Autowired ApplicationContext context; @Bean public CircularDependencyA getCircularDependencyA() { return new CircularDependencyA(); } @Bean public CircularDependencyB getCircularDependencyB() { return new CircularDependencyB(); } @Test public void givenCircularDependency_whenSetterInjection_thenItWorks() { CircularDependencyA circA = context.getBean(CircularDependencyA.class); Assert.assertEquals("Hi!", circA.getCircB().getMessage()); } }
4.4 使用@PostConstruct
@Component public class CircularDependencyA { @Autowired private CircularDependencyB circB; @PostConstruct public void init() { circB.setCircA(this); } public CircularDependencyB getCircB() { return circB; } }
@Component public class CircularDependencyB { private CircularDependencyA circA; private String message = "Hi!"; public void setCircA(CircularDependencyA circA) { this.circA = circA; } public String getMessage() { return message; }
4.5 實現(xiàn)ApplicationContextAware與InitializingBean
@Component public class CircularDependencyA implements ApplicationContextAware, InitializingBean { private CircularDependencyB circB; private ApplicationContext context; public CircularDependencyB getCircB() { return circB; } @Override public void afterPropertiesSet() throws Exception { circB = context.getBean(CircularDependencyB.class); } @Override public void setApplicationContext(final ApplicationContext ctx) throws BeansException { context = ctx; } }
@Component public class CircularDependencyB { private CircularDependencyA circA; private String message = "Hi!"; @Autowired public void setCircA(CircularDependencyA circA) { this.circA = circA; } public String getMessage() { return message; } }
5.總結(jié)
處理循環(huán)依賴有多種方式。首先考慮是否能夠通過重新設(shè)計依賴來避免循環(huán)依賴。如果確實需要循環(huán)依賴,那么可以通過前文提到的方式來處理。優(yōu)先建議使用setter注入來解決。
以上這篇基于SpringBoot構(gòu)造器注入循環(huán)依賴及解決方式就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
windows系統(tǒng)IIS部署Django項目的實踐
采用IIS服務(wù)器部署相比django提供的開發(fā)者服務(wù)器具有更好的并發(fā)訪問能力,性能更加穩(wěn)定,本文主要介紹了windows系統(tǒng)IIS部署Django項目的實踐,具有一定的參考價值,感興趣的可以了解一下2022-03-03Python: tkinter窗口屏幕居中,設(shè)置窗口最大,最小尺寸實例
這篇文章主要介紹了Python: tkinter窗口屏幕居中,設(shè)置窗口最大,最小尺寸實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03Python中導(dǎo)入自定義模塊的幾種方法總結(jié)
這篇文章主要介紹了Python中導(dǎo)入自定義模塊的幾種方法總結(jié),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-01-01