基于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 相关文章推荐
精确查找PHP WEBSHELL木马的方法(1)
Apr 12 Python
从零学python系列之教你如何根据图片生成字符画
May 23 Python
零基础写python爬虫之爬虫的定义及URL构成
Nov 04 Python
在Django中同时使用多个配置文件的方法
Jul 22 Python
详解Python中dict与set的使用
Aug 10 Python
ubuntu安装sublime3并配置python3环境的方法
Mar 15 Python
浅谈PySpark SQL 相关知识介绍
Jun 14 Python
Python numpy.zero() 初始化矩阵实例
Nov 27 Python
python中的socket实现ftp客户端和服务器收发文件及md5加密文件
Apr 01 Python
Django 设置admin后台表和App(应用)为中文名的操作方法
May 10 Python
Python基于Socket实现简易多人聊天室的示例代码
Nov 29 Python
k-means & DBSCAN 总结
Apr 27 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
PHP中的MYSQL常用函数(php下操作数据库必备)
2010/09/12 PHP
php给图片添加文字水印方法汇总
2015/08/27 PHP
php实现微信支付之退款功能
2018/05/30 PHP
PHP随机生成中文段落示例【测试网站内容时使用】
2020/04/26 PHP
利用PHP内置SERVER开启web服务(本地开发使用)
2021/03/09 PHP
利用JS进行图片的切换即特效展示图片
2013/12/03 Javascript
jquery学习总结(超级详细)
2014/09/04 Javascript
jquery获取radio值实例
2014/10/16 Javascript
深入浅出分析javaScript中this用法
2015/05/09 Javascript
详解JavaScript中数组的相关知识
2015/07/29 Javascript
原生javascript实现匀速运动动画效果
2016/02/26 Javascript
浅谈JavaScript对象与继承
2016/07/10 Javascript
js截取字符串功能的实现方法
2017/09/27 Javascript
angularjs 动态从后台获取下拉框的值方法
2018/08/13 Javascript
jQuery easyui datagird编辑行删除行功能的实现代码
2018/09/20 jQuery
Python实现的几个常用排序算法实例
2014/06/16 Python
python抓取网页时字符集转换问题处理方案分享
2014/06/19 Python
python实现无证书加密解密实例
2014/10/27 Python
使用Python的Flask框架实现视频的流媒体传输
2015/03/31 Python
Python中asyncore异步模块的用法及实现httpclient的实例
2016/06/28 Python
详细解读tornado协程(coroutine)原理
2018/01/15 Python
Python实现的删除重复文件或图片功能示例【去重】
2019/04/23 Python
基于python 微信小程序之获取已存在模板消息列表
2019/08/05 Python
Python3加密解密库Crypto的RSA加解密和签名/验签实现方法实例
2020/02/11 Python
Python内置异常类型全面汇总
2020/05/28 Python
Haglöfs瑞典官方网站:haglofs火柴棍,欧洲顶级户外品牌
2018/10/18 全球购物
Prototype如何实现页面局部定时刷新
2013/08/06 面试题
出纳岗位职责模板
2013/11/27 职场文书
师范生的个人求职信范文
2014/01/04 职场文书
期末学生评语大全
2014/04/24 职场文书
八项规定对照检查材料
2014/08/31 职场文书
教师工作表现评语
2014/12/31 职场文书
地道战观后感300字
2015/06/04 职场文书
2016年寒假生活小结
2015/10/10 职场文书
Python 内置函数速查表一览
2021/06/02 Python
React 并发功能体验(前端的并发模式)
2021/07/01 Javascript