基于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生成随机MAC地址
Mar 10 Python
Python设计模式编程中解释器模式的简单程序示例分享
Mar 02 Python
详谈Python高阶函数与函数装饰器(推荐)
Sep 30 Python
python DataFrame 修改列的顺序实例
Apr 10 Python
python生成九宫格图片
Nov 19 Python
用Python画一个LinkinPark的logo代码实例
Sep 10 Python
使用python写一个自动浏览文章的脚本实例
Dec 05 Python
用OpenCV将视频分解成单帧图片,图片合成视频示例
Dec 10 Python
使用 prometheus python 库编写自定义指标的方法(完整代码)
Jun 29 Python
python 读txt文件,按‘,’分割每行数据操作
Jul 05 Python
Python如何读写字节数据
Aug 05 Python
Python使用openpyxl批量处理数据
Jun 23 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
乐信RP2100的电路分析和打磨
2021/03/02 无线电
PHP脚本的10个技巧(4)
2006/10/09 PHP
php判断并删除空目录及空子目录的方法
2015/02/11 PHP
微信公众平台开发之天气预报功能
2015/08/31 PHP
thinkphp框架实现数据添加和显示功能
2016/06/29 PHP
php获取访问者浏览页面的浏览器类型
2017/01/23 PHP
PHP实现的堆排序算法详解
2017/08/17 PHP
PHP多线程模拟实现秒杀抢单
2018/02/07 PHP
javascript(jquery)利用函数修改全局变量的代码
2009/11/02 Javascript
jquery JSON的解析方式示例介绍
2014/07/27 Javascript
jQuery中大家不太了解的几个方法
2015/03/04 Javascript
Bootstrap缩略图与警告框学习使用
2017/02/08 Javascript
vue项目中v-model父子组件通信的实现详解
2017/12/10 Javascript
vue.js 获取select中的value实例
2018/03/01 Javascript
微信小程序数据统计和错误统计的实现方法
2019/06/26 Javascript
微信小程序在线客服自动回复功能(基于node)
2019/07/03 Javascript
Vue 中获取当前时间并实时刷新的实现代码
2020/05/12 Javascript
javascript中导出与导入实现模块化管理教程
2020/12/03 Javascript
[51:26]VP vs VG 2018国际邀请赛小组赛BO2 第二场 8.19
2018/08/21 DOTA
python实现的正则表达式功能入门教程【经典】
2017/06/05 Python
python中强大的format函数实例详解
2018/12/05 Python
浅谈python中真正关闭socket的方法
2018/12/18 Python
获取django框架orm query执行的sql语句实现方法分析
2019/06/20 Python
Transpose 数组行列转置的限制方式
2020/02/11 Python
Python列表去重复项的N种方法(实例代码)
2020/05/12 Python
详解HTML5中的Communication API基本使用方法
2016/01/29 HTML / CSS
Html5导航栏吸顶方案原理与对比实现
2020/06/10 HTML / CSS
英国最大的在线奢侈手表零售商:Jura Watches
2018/01/29 全球购物
家得宝官网:The Home Depot(全球最大的家居装饰专业零售商)
2018/12/17 全球购物
美国在线家具网站:GDFStudio
2021/03/13 全球购物
学校百日安全活动总结
2015/05/07 职场文书
2015年安全生产月工作总结
2015/07/27 职场文书
学会感恩主题班会
2015/08/12 职场文书
MySQL数据库索引的最左匹配原则
2021/11/20 MySQL
斗罗大陆八大特殊魂兽,龙族始祖排榜首,第五最残忍(翠魔鸟)
2022/03/18 国漫
vue封装数字翻牌器
2022/04/20 Vue.js