基于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 相关文章推荐
Python django实现简单的邮件系统发送邮件功能
Jul 14 Python
简述Python2与Python3的不同点
Jan 21 Python
python 多线程重启方法
Feb 18 Python
Python3.5实现的三级菜单功能示例
Mar 25 Python
详解如何在cmd命令窗口中搭建简单的python开发环境
Aug 29 Python
python 微信好友特征数据分析及可视化
Jan 07 Python
Python基于Dlib的人脸识别系统的实现
Feb 26 Python
Tensorflow安装问题: Could not find a version that satisfies the requirement tensorflow
Apr 20 Python
基于Python第三方插件实现西游记章节标注汉语拼音的方法
May 22 Python
python中的垃圾回收(GC)机制
Sep 21 Python
Python+unittest+DDT实现数据驱动测试
Nov 30 Python
Python将CSV文件转化为HTML文件的操作方法
Jun 30 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中一个完整表单处理实现代码
2011/11/10 PHP
php实现的获取网站备案信息查询代码(360)
2013/09/23 PHP
Yii2 assets清除缓存的方法
2016/05/16 PHP
PHP Ajax JavaScript Json获取天气信息实现代码
2016/08/17 PHP
php实现博客,论坛图片防盗链的方法
2016/10/15 PHP
分析php://output和php://stdout的区别
2018/05/06 PHP
JavaScript为对象原型prototype添加属性的两种方式
2010/08/01 Javascript
jquery ui对话框实例代码
2013/05/10 Javascript
Javascript玩转继承(三)
2014/05/08 Javascript
jquery+json实现动态商品内容展示的方法
2016/01/14 Javascript
获取当前月(季度/年)的最后一天(set相关操作及应用)
2016/12/27 Javascript
bootstrap 通过加减按钮实现输入框组功能
2017/11/15 Javascript
详解angular2.x创建项目入门指令
2018/10/11 Javascript
分享JS表单验证源码(带错误提示及密码等级)
2020/01/05 Javascript
原生js实现自定义滚动条组件
2021/01/20 Javascript
利用Python脚本生成sitemap.xml的实现方法
2017/01/31 Python
Python学习教程之常用的内置函数大全
2017/07/14 Python
numpy中实现ndarray数组返回符合特定条件的索引方法
2018/04/17 Python
Python获取系统所有进程PID及进程名称的方法示例
2018/05/24 Python
wxPython的安装与使用教程
2018/08/31 Python
Flask框架请求钩子与request请求对象用法实例分析
2019/11/07 Python
Python:type、object、class与内置类型实例
2019/12/25 Python
python GUI库图形界面开发之PyQt5信号与槽的高级使用技巧(自定义信号与槽)详解与实例
2020/03/06 Python
Python如何脚本过滤文件中的注释
2020/05/27 Python
python tkiner实现 一个小小的图片翻页功能的示例代码
2020/06/24 Python
Python getsizeof()和getsize()区分详解
2020/11/20 Python
意大利宠物用品购物网站:Bauzaar
2018/09/15 全球购物
MyHeritage美国:家族史研究和DNA测试的领先服务
2019/05/27 全球购物
Ray-Ban雷朋奥地利官网:全球领先的太阳眼镜品牌
2020/10/12 全球购物
计算机求职信
2013/12/01 职场文书
毕业生大学生活自我总结
2014/01/31 职场文书
社区个人对照检查材料(群众路线)
2014/09/26 职场文书
2014年向国旗敬礼活动方案
2014/09/27 职场文书
2016特色励志班级口号
2015/12/24 职场文书
浅谈Python中的正则表达式
2021/06/28 Python
PYTHON基于Pyecharts绘制常见的直角坐标系图表
2022/04/28 Python