欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

基于SpringBoot构造器注入循环依赖及解决方式

程序员文章站 2022-06-14 14:25:39
1. 循环依赖是什么? bean a 依赖 b,bean b 依赖 a这种情况下出现循环依赖。bean a → bean b → bean a更复杂的间接依赖造成的循环依赖如下。bean a → be...

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构造器注入循环依赖及解决方式就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。