Spring解決循環(huán)依賴問題的四種方法匯總
Spring解決循環(huán)依賴問題的四種方法
- @Lazy方式
- 使用Setter/Field Injection
- 使用
@PostConstruct - 實現(xiàn) ApplicationContextAware 和 InitializingBean
不管使用那種方式,最佳的還是通過調(diào)整代碼結(jié)構(gòu),從根上設(shè)計從而達(dá)到避免。
定義兩個相互依賴的 bean(通過構(gòu)造函數(shù)注入)
package icu.kevin.springpart.circular.constructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CircularDependencyA {
private CircularDependencyB circB;
@Autowired
public CircularDependencyA(CircularDependencyB circB) {
this.circB = circB;
}
}package icu.kevin.springpart.circular.constructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CircularDependencyB {
private CircularDependencyA circA;
@Autowired
public CircularDependencyB(CircularDependencyA circA) {
this.circA = circA;
}
}package icu.kevin.springpart.circular.constructor;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = { "icu.kevin.springpart.circular.constructor" })
public class TestConfig {
}編寫測試用例,測試是否存在循環(huán)依賴
package icu.kevin.springpart.circular.constructor;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { TestConfig.class })
public class CircularDependencyIntegrationTest {
// Error creating bean with name 'circularDependencyA': Requested bean is currently in creation: Is there an unresolvable circular reference?
@Test
public void givenCircularDependency_whenConstructorInjection_thenItFails() {
// 測試可以為空,因為在上下文加載期間將檢測到循環(huán)依賴關(guān)系
}
}@Lazy方式
@Lazy告訴 Spring 延遲初始化其中一個 bean。因此,它不會完全初始化 Bean,而是創(chuàng)建一個代理將其注入另一個 Bean。注入的 Bean 只有在第一次需要時才會完全創(chuàng)建。
@Lazy 注解是通過建立一個中間代理層,來破解循環(huán)依賴的。
package icu.kevin.springpart.circular.lazy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@Component
public class CircularDependencyA {
private CircularDependencyB circB;
@Autowired
public CircularDependencyA(@Lazy CircularDependencyB circB) {
this.circB = circB;
}
}package icu.kevin.springpart.circular.lazy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CircularDependencyB {
private CircularDependencyA circA;
@Autowired
public CircularDependencyB(CircularDependencyA circA) {
this.circA = circA;
}
}package icu.kevin.springpart.circular.lazy;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = { "icu.kevin.springpart.circular.lazy" })
public class TestConfig {
}package icu.kevin.springpart.circular.lazy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { TestConfig.class })
public class CircularDependencyIntegrationTest {
// Error creating bean with name 'circularDependencyA': Requested bean is currently in creation: Is there an unresolvable circular reference?
@Test
public void givenCircularDependency_whenConstructorInjection_thenItFails() {
// 測試可以為空,因為在上下文加載期間將檢測到循環(huán)依賴關(guān)系
}
}啟動可以看出,在上下文檢查循環(huán)依賴是可以正常通過的
查看源碼ContextAnnotationAutowireCandidateResolver#getLazyResolutionProxyIfNecessary首先會調(diào)用 isLazy 去判斷一下是否需要延遲加載,
如果需要,則調(diào)用 buildLazyResolutionProxy 方法構(gòu)建一個延遲加載的對象;
使用Setter/Field Injection
在setter注入的時候發(fā)生的依賴循環(huán)是可以被解決的,但只能解決單例情況的循環(huán)依賴。
對于setter注入造成的依賴是通過Spring容器提前暴露剛剛完成構(gòu)造器但還沒有進(jìn)行注入的bean來完成的。通過提前暴露一個單例工廠讓其他的bean可以引用到該Bean。
package icu.kevin.springpart.circular.setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CircularDependencyA {
private CircularDependencyB circB;
public CircularDependencyB getCircB() {
return circB;
}
@Autowired
public void setCircB(CircularDependencyB circB) {
this.circB = circB;
}
}package icu.kevin.springpart.circular.setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CircularDependencyB {
private CircularDependencyA circA;
private String message = "Hi!";
public CircularDependencyA getCircA() {
return circA;
}
@Autowired
public void setCircA(CircularDependencyA circA) {
this.circA = circA;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}package icu.kevin.springpart.circular.setter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = { "icu.kevin.springpart.circular.setter" })
public class TestConfig {
}package icu.kevin.springpart.circular.setter;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { TestConfig.class })
public class CircularDependencyIntegrationTest {
@Autowired
private ApplicationContext context;
@Test
public void givenCircularDependency_whenConstructorInjection_thenItFails() {
// Empty test; we just want the context to load
CircularDependencyA circA = context.getBean(CircularDependencyA.class);
Assert.assertEquals("Hi!", circA.getCircB().getMessage());
}
}使用@PostConstruct
package icu.kevin.springpart.circular.postConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class CircularDependencyA {
@Autowired
private CircularDependencyB circB;
@PostConstruct
public void init(){
circB.setCircA(this);
}
public CircularDependencyB getCircB() {
return circB;
}
}package icu.kevin.springpart.circular.postConstruct;
import org.springframework.stereotype.Component;
@Component
public class CircularDependencyB {
private CircularDependencyA circA;
private String message = "Hi!";
public CircularDependencyA getCircA() {
return circA;
}
public void setCircA(CircularDependencyA circA) {
this.circA = circA;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}package icu.kevin.springpart.circular.postConstruct;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = { "icu.kevin.springpart.circular.postConstruct" })
public class TestConfig {
}package icu.kevin.springpart.circular.postConstruct;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { TestConfig.class })
public class CircularDependencyIntegrationTest {
@Autowired
private ApplicationContext context;
@Test
public void givenCircularDependency_whenConstructorInjection_thenItFails() {
//
CircularDependencyA circA = context.getBean(CircularDependencyA.class);
Assert.assertEquals("Hi!", circA.getCircB().getMessage());
}
}實現(xiàn) ApplicationContextAware 和 InitializingBean
如果其中一個 Bean 實現(xiàn)了 ApplicationContextAware,則該 Bean 可以訪問 Spring 上下文,并可以從那里提取另一個 Bean。
通過實現(xiàn) InitializingBean,我們指示該 Bean 在設(shè)置完所有屬性后必須執(zhí)行一些操作。在這種情況下,我們要手動設(shè)置依賴項。
package icu.kevin.springpart.circular.initializingBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@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;
}
}package icu.kevin.springpart.circular.initializingBean;
import org.springframework.stereotype.Component;
@Component
public class CircularDependencyB {
private CircularDependencyA circA;
private String message = "Hi!";
public CircularDependencyA getCircA() {
return circA;
}
public void setCircA(CircularDependencyA circA) {
this.circA = circA;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}package icu.kevin.springpart.circular.initializingBean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = { "icu.kevin.springpart.circular.initializingBean" })
public class TestConfig {
}package icu.kevin.springpart.circular.initializingBean;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { TestConfig.class })
public class CircularDependencyIntegrationTest {
@Autowired
private ApplicationContext context;
@Test
public void givenCircularDependency_whenConstructorInjection_thenItFails() {
// Empty test; we just want the context to load
CircularDependencyA circA = context.getBean(CircularDependencyA.class);
Assert.assertEquals("Hi!", circA.getCircB().getMessage());
}
}到此這篇關(guān)于Spring解決循環(huán)依賴問題的四種方法的文章就介紹到這了,更多相關(guān)Spring循環(huán)依賴內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Maven構(gòu)建SpringBoot集成MyBatis過程
文章介紹了如何使用Maven創(chuàng)建一個Spring Boot項目,并集成MyBatis,首先,配置Maven環(huán)境,然后在pom.xml中添加MyBatis依賴,配置數(shù)據(jù)庫連接信息,最后生成Mapper XML文件和DAO層代碼2024-11-11
Java中的static關(guān)鍵字用法總結(jié)
這篇文章主要介紹了Java中的static關(guān)鍵字用法總結(jié),static是Java50個關(guān)鍵字之一,static關(guān)鍵字可以用來修飾代碼塊表示靜態(tài)代碼塊,修飾成員變量表示全局靜態(tài)成員變量,修飾方法表示靜態(tài)方法,需要的朋友可以參考下2023-11-11
Spring Web項目spring配置文件隨服務(wù)器啟動時自動加載
這篇文章主要介紹了Spring Web項目spring配置文件隨服務(wù)器啟動時自動加載,加載spring的配置文件,并且只加載一次,從而提高程序效率。具體內(nèi)容詳情大家通過本文一起學(xué)習(xí)吧2018-01-01
MessageUtils.message("user.jcaptcha.expire")問題及解決
文章介紹了若依項目中的國際化工具M(jìn)essageUtils,用于根據(jù)不同語言環(huán)境獲取驗證碼過期提示信息,通過Spring的MessageSource讀取多語言配置文件實現(xiàn),并詳細(xì)描述了其作用、配置文件位置、底層原理及觸發(fā)場景2026-04-04

