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


Posted in Python onApril 26, 2020

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

Python 相关文章推荐
Python3基础之条件与循环控制实例解析
Aug 13 Python
python实现单线程多任务非阻塞TCP服务端
Jun 13 Python
Python实现两款计算器功能示例
Dec 19 Python
多个应用共存的Django配置方法
May 30 Python
python 执行文件时额外参数获取的实例
Dec 18 Python
Python线程池模块ThreadPoolExecutor用法分析
Dec 28 Python
Python3.5常见内置方法参数用法实例详解
Apr 29 Python
pyqt5、qtdesigner安装和环境设置教程
Sep 25 Python
Xadmin+rules实现多选行权限方式(级联效果)
Apr 07 Python
使用pygame实现垃圾分类小游戏功能(已获校级二等奖)
Jul 23 Python
Python常用base64 md5 aes des crc32加密解密方法汇总
Nov 06 Python
Matplotlib绘制条形图的方法你知道吗
Mar 21 Python
Python判断字符串是否为空和null方法实例
Apr 26 #Python
如何将PySpark导入Python的放实现(2种)
Apr 26 #Python
基于python实现对文件进行切分行
Apr 26 #Python
python matplotlib模块基本图形绘制方法小结【直线,曲线,直方图,饼图等】
Apr 26 #Python
简单了解Java Netty Reactor三种线程模型
Apr 26 #Python
Python Selenium截图功能实现代码
Apr 26 #Python
使用Pycharm(Python工具)新建项目及创建Python文件的教程
Apr 26 #Python
You might like
通过curl模拟post和get方式提交的表单类
2014/04/23 PHP
实现php删除链表中重复的结点
2018/09/27 PHP
jquery tools之tabs 选项卡/页签
2009/07/25 Javascript
Javascript this指针
2009/07/30 Javascript
JavaScript 异步调用框架 (Part 1 - 问题 & 场景)
2009/08/03 Javascript
js原生态函数中使用jQuery中的 $(this)无效的解决方法
2011/05/25 Javascript
解析jquery获取父窗口的元素
2013/06/26 Javascript
jquery异步跨域访问代码
2013/06/28 Javascript
JavaScript前端图片加载管理器imagepool使用详解
2014/12/29 Javascript
js对象的复制继承实例
2015/01/10 Javascript
jQuery中empty()方法用法实例
2015/01/16 Javascript
JavaScript将数字转换成大写中文的方法
2015/03/23 Javascript
页面内容排序插件jSort使用方法
2015/10/10 Javascript
Angularjs根据json文件动态生成路由状态的实现方法
2017/04/17 Javascript
vue的keep-alive中使用EventBus的方法
2019/04/23 Javascript
通过图带你深入了解vue的响应式原理
2019/06/21 Javascript
使用Vue调取接口,并渲染数据的示例代码
2019/10/28 Javascript
JS实现canvas简单小画板功能
2020/06/23 Javascript
js实现简单的点名器随机色实例代码
2020/09/20 Javascript
python创建和使用字典实例详解
2013/11/01 Python
使用Python压缩和解压缩zip文件的教程
2015/05/06 Python
python实现图片处理和特征提取详解
2017/11/13 Python
Python创建字典的八种方式
2019/02/27 Python
18个Python脚本可加速你的编码速度(提示和技巧)
2019/10/17 Python
Python反爬虫伪装浏览器进行爬虫
2020/02/28 Python
浅谈django channels 路由误导
2020/05/28 Python
Python selenium爬取微信公众号文章代码详解
2020/08/12 Python
HTML5响应式(自适应)网页设计的实现
2017/11/17 HTML / CSS
iHerb中文官网:维生素、保健品和健康产品
2018/11/01 全球购物
三八妇女节活动总结
2014/05/04 职场文书
服务口号大全
2014/06/11 职场文书
慰问信格式规范
2015/03/23 职场文书
学校开除通知书
2015/04/25 职场文书
MySQL 使用索引扫描进行排序
2021/06/20 MySQL
Python Pandas数据分析之iloc和loc的用法详解
2021/11/11 Python
Go语言编译原理之变量捕获
2022/08/05 Golang