1. 程式人生 > 程式設計 >基於SpringBoot構造器注入迴圈依賴及解決方式

基於SpringBoot構造器注入迴圈依賴及解決方式

1. 迴圈依賴是什麼?

Bean A 依賴 B,Bean B 依賴 A這種情況下出現迴圈依賴。

Bean A → Bean B → Bean A

更復雜的間接依賴造成的迴圈依賴如下。

Bean A → Bean B → Bean C → Bean D → Bean E → Bean A

2. 迴圈依賴會產生什麼結果?

當Spring正在載入所有Bean時,Spring嘗試以能正常建立Bean的順序去建立Bean。

例如,有如下依賴:

Bean A → Bean B → Bean C

Spring先建立beanC,接著建立bean B(將C注入B中),最後建立bean A(將B注入A中)。

但當存在迴圈依賴時,Spring將無法決定先建立哪個bean。這種情況下,Spring將產生異常BeanCurrentlyInCreationException。

當使用構造器注入時經常會發生迴圈依賴問題。如果使用其它型別的注入方式能夠避免這種問題。

3. 構造器注入迴圈依賴例項

首先定義兩個相互通過構造器注入依賴的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將會產生異常:

BeanCurrentlyInCreationException: Error creating bean with name ‘circularDependencyA': Requested bean is currently in creation: Is there an unresolvable circular reference?

4.解決方法

處理這種問題目前有如下幾種常見方式。

4.1 重新設計

重新設計結構,消除迴圈依賴。

4.2 使用註解 @Lazy

一種最簡單的消除迴圈依賴的方式是通過延遲載入。在注入依賴時,先注入代理物件,當首次使用時再建立物件完成注入。

@Component
public class CircularDependencyA {
 
 private CircularDependencyB circB;
 
 @Autowired
 public CircularDependencyA(@Lazy CircularDependencyB circB) {
  this.circB = circB;
 }
}

使用@Lazy後,執行程式碼,可以看到異常消除。

4.3 使用Setter/Field注入

Spring文件建議的一種方式是使用setter注入。當依賴最終被使用時才進行注入。對前文的樣例程式碼少做修改,來觀察測試效果。

@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 實現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.總結

處理迴圈依賴有多種方式。首先考慮是否能夠通過重新設計依賴來避免迴圈依賴。如果確實需要迴圈依賴,那麼可以通過前文提到的方式來處理。優先建議使用setter注入來解決。

以上這篇基於SpringBoot構造器注入迴圈依賴及解決方式就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援我們。