基于springboot构造器注入循环依赖及解决方式-kb88凯时官网登录

时间:2020-04-26
阅读:
免费资源网 - https://freexyz.cn/

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注入来解决。

免费资源网 - https://freexyz.cn/
返回顶部
顶部
网站地图