springboot的循环依赖问题描述及解决方案
一.了解循环依赖的场景
在Spring Boot中,循环依赖是指两个或多个Bean之间相互依赖,导致它们无法正确地创建和注入。循环依赖可能会导致应用程序无法启动或出现其他异常。
在以下情况下,您可能需要显式设置循环依赖:
- 两个Bean相互依赖:当两个Bean相互依赖,并且没有其他Bean可以打破这种依赖关系时,您需要显式设置循环依赖。例如,类A依赖于类B,而类B又依赖于类A。
- 使用
@Autowired
注解:当您使用@Autowired
注解将一个Bean注入到另一个Bean中时,如果它们之间存在循环依赖,您需要显式设置循环依赖。
下面是一个简单的流程图和示意图来解释循环依赖:
流程图:
java复制代码
Start -> A -> B -> A (循环依赖) -> Error |
在这个例子中,类A依赖于类B,类B又依赖于类A,形成了一个循环依赖关系。如果没有显式设置循环依赖,Spring容器在启动时就会抛出异常,因为无法正确地创建和注入这两个Bean。
为了解决这个问题,您可以使用@Autowired
注解显式设置循环依赖。这样做可以让Spring容器自动处理循环依赖关系,并确保这两个Bean能够正确地创建和注入。例如:
java复制代码
@Service public class A { @Autowired private B b; } @Service public class B { @Autowired private A a; }
在这个例子中,通过使用@Autowired
注解显式设置循环依赖,Spring容器可以正确地创建和注入类A和类B的实例。
二.如何判断系统是否产生了循环依赖
判断系统是否产生了循环依赖,可以通过以下几种方法进行检测:
- 观察法:观察系统中的依赖关系,看是否存在某个或某些Bean反复依赖其他Bean,并且无法正常完成初始化,这可能是循环依赖的迹象。
- 日志法:在系统中的关键位置添加日志记录,跟踪Bean的创建和注入过程。如果发现日志中存在循环依赖的线索,例如多个Bean相互依赖导致的创建顺序循环,则可以确定存在循环依赖问题。
- 调试法:使用调试工具,例如IDE的调试功能,设置断点并逐步执行代码,以观察是否存在循环依赖的情况。在调试过程中,可以检查调用栈和变量信息,看是否存在多个Bean相互等待对方完成初始化的情况。
- 工具法:使用专门的循环依赖检测工具,例如Java字节码分析工具如ASM、Javassist等,来检测系统中的循环依赖问题。根据工具的输出结果,可以确定是否存在循环依赖问题。
- 程序代码法:编写程序代码进行循环依赖检测。可以使用Java语言或其他编程语言编写循环依赖检测程序,利用反射机制获取系统中的Bean信息,并检查它们之间的依赖关系是否存在循环。
具体步骤如下:
以下是上述5种方法的详细步骤及使用例子:
- 观察法:
步骤:
- 在系统运行过程中,观察应用程序的行为。
- 特别注意那些无法正常初始化的Bean,看它们是否在等待其他Bean的初始化。
- 观察日志输出,查找循环依赖的相关信息。
例子:
假设有两个Bean A和B相互依赖,当Bean A尝试初始化时,它需要依赖Bean B的实例,而Bean B的初始化又需要Bean A的实例。这就会导致循环依赖的问题。通过观察应用程序的行为和日志输出,可以发现Bean A和Bean B在初始化时相互等待,无法正常完成初始化。
- 日志法:
步骤:
- 在应用程序的关键位置添加日志记录,例如在Bean的初始化方法中。
- 运行应用程序并观察日志输出。
- 查找循环依赖的线索,例如多个Bean相互依赖导致的创建顺序循环。
例子:
在Spring框架中,可以在Bean的初始化方法中添加日志记录,例如:
java复制代码
@Component
public class ExampleBean { @Autowired private AnotherBean anotherBean; public void init() { System.out.println("Creating exampleBean..."); // 其他初始化代码 }
}
在日志输出中,可以观察到类似以下信息:Creating exampleBean...Creating anotherBean...Creating exampleBean...Creating anotherBean...(无限循环)
这说明存在循环依赖的问题。
- 调试法:
步骤:
- 使用调试工具打开应用程序,例如在IDE中打开调试视图。
- 在调试视图中找到与Bean创建和注入相关的类或方法,并设置断点。
- 继续运行应用程序,当断点触发时,调试工具将暂停执行并允许你检查当前的变量、调用栈等信息。
- 观察调用栈和变量信息,查找是否存在多个Bean相互等待对方完成初始化的情况。
例子:
在IDE中打开调试视图,找到与Bean创建和注入相关的类或方法,例如在Spring框架中的ApplicationContext
类,并设置断点。继续运行应用程序,当断点触发时,查看调用栈信息。如果发现调用栈中存在多个Bean相互等待对方完成初始化的情况,则说明存在循环依赖的问题。
- 工具法:
步骤:
- 选择一个适合的循环依赖检测工具,例如Java字节码分析工具如ASM、Javassist等。
- 根据工具的文档或API使用指南,编写自定义的检测脚本或程序。例如,可以使用ASM工具附带的API编写一个检测循环依赖的程序。
三.解决循环依赖的几种方式
Spring Boot解决循环依赖的方法有多种,以下为每种方法提供详细解释和示例:
- 使用构造器注入:
构造器注入是一种在构造器中通过参数传递依赖项的方式。这种方法可以确保所有依赖项在实例化Bean时就已经准备好,从而避免循环依赖的问题。例如:
java复制代码
@Service
public class A {private final B b;@Autowiredpublic A(B b) {this.b = b;}
}@Service
public class B {private final A a;@Autowiredpublic B(A a) {this.a = a;}
}
在这个例子中,通过使用构造器注入,我们可以在创建Bean实例时将依赖关系注入进去,避免了循环依赖问题。
2.使用setter注入:
setter注入是在Bean属性上使用setter方法注入依赖项的方式。这种方式通常会导致循环依赖问题,因为它是在实例化Bean后注入依赖项的。如果两个Bean都依赖于对方,并且都使用setter注入,就会形成一个循环依赖链。然而,在某些情况下,setter注入可能是必要的。此时,可以考虑将setter注入更改为构造器注入。
3.使用@Lazy
注解:
@Lazy
注解可以让Spring在需要时延迟加载Bean。当您使用@Lazy
注解时,Spring会推迟初始化Bean,直到您首次使用该Bean时才创建它。这样可以让您避免循环依赖问题,因为Spring会按照依赖关系自动初始化Bean。例如:
java复制代码
@Service
public class A { private final BService bService; @Autowired public A(@Lazy BService bService) { this.bService = bService; }
}
在这个例子中,使用@Lazy
注解可以避免循环依赖问题,因为Spring会延迟加载BService
Bean。
4.使用@DependsOn
注解:
@DependsOn
注解可以让您指定一个或多个Bean的依赖关系。这样,Spring容器会先创建依赖的Bean,再创建被依赖的Bean,从而避免循环依赖的问题。例如:
java复制代码
@Service(dependsOn = "otherBean") public class MyBean { }
在这个例子中,MyBean
将等待名为"otherBean"的Bean初始化完成后才进行初始化。这样可以避免循环依赖的问题。
5. 修改配置:如果以上方法仍然无法解决问题,可以尝试修改Spring Boot的配置。例如,将spring.main.dependency-check
属性设置为true
,这样可以自动检测循环依赖问题并报错。此外,还可以尝试使用代理模式来避免循环依赖的问题。例如,使用JDK动态代理或CGLIB代理来代理循环依赖的Bean。这样,当一个Bean依赖另一个Bean时,使用一个代理对象代替被依赖的Bean,这个代理对象在被依赖的Bean完全创建之前暂时代替被依赖的Bean。
6.三级缓存机制
三级缓存是Spring框架中解决循环依赖问题的机制。它包括内存缓存、本地缓存和网络缓存三个级别。Spring Boot的三级缓存包含:singletonObjects、earlySingletonObjects和singletonFactories。
具体使用方法如下:
- 在Spring容器中,当一个Bean需要注入另一个Bean时,Spring会先检查一级缓存(内存缓存)中是否已经存在该Bean的实例。如果存在,则直接注入;如果不存在,则继续检查二级缓存(本地缓存)和三级缓存(网络缓存)。
- 如果在二级缓存中找到了该Bean的实例,则将其暴露给当前Bean,并放入一级缓存中。这个过程被称为“提前暴露”,可以解决循环依赖问题。
- 如果在三级缓存中找到了该Bean的工厂对象,则通过工厂对象创建新的Bean实例,并将其放入二级缓存中。然后,将该Bean实例暴露给当前Bean,并放入一级缓存中。
- 如果在三级缓存中没有找到工厂对象,则执行该Bean的实例化操作,并将其放入三级缓存中。然后,将该Bean实例暴露给当前Bean,并放入一级缓存中。
- 通过以上步骤,可以确保每个Bean只会被创建一次,避免了循环依赖问题。同时,三级缓存的设计也确保了每个Bean只会被创建一次,从而避免了重复创建同一个Bean的问题。
四.解决循环依赖方法原理
-
构造函数注入 构造函数注入是一种常见的解决循环依赖的方法。通过将依赖作为参数传递给构造函数,我们可以避免循环依赖的发生。这是因为构造函数注入是在对象创建时发生的,而不是在对象依赖被解析时发生的。这种方法的原理是通过将依赖作为参数传递给构造函数,使得对象的创建和依赖的解析分开进行。
-
Setter方法注入 Setter方法注入是另一种常用的解决循环依赖的方法。通过将依赖通过Setter方法注入到对象中,我们可以避免循环依赖的问题。这是因为Setter方法注入是在对象创建后发生的,而不是在对象依赖被解析时发生的。这种方法的原理是通过将依赖通过Setter方法注入到对象中,使得对象的创建和依赖的解析分开进行。
-
使用@Lazy注解 @Lazy注解是Spring框架提供的一种解决循环依赖的方法。通过在Bean上添加@Lazy注解,我们可以延迟依赖的解析,从而避免循环依赖的发生。这是因为@Lazy注解告诉Spring在需要使用依赖时再进行解析,而不是在对象创建时就进行解析。这种方法的原理是通过延迟依赖的解析,使得对象的创建和依赖的解析分开进行。
-
使用@DependsOn注解 @DependsOn注解是另一种解决循环依赖的方法。通过在Bean上添加@DependsOn注解,我们可以指定Bean的依赖顺序,从而避免循环依赖的问题。这是因为@DependsOn注解告诉Spring在创建Bean时先创建指定的依赖Bean,然后再创建当前Bean。这种方法的原理是通过指定依赖的创建顺序,使得对象的创建和依赖的解析分开进行。
-
使用@PostConstruct注解 @PostConstruct注解是Spring框架提供的一种解决循环依赖的方法。通过在Bean的初始化方法上添加@PostConstruct注解,我们可以在Bean创建完成后执行一些初始化操作,从而避免循环依赖的问题。这是因为@PostConstruct注解告诉Spring在创建Bean后立即执行指定的初始化方法。这种方法的原理是通过在Bean创建完成后执行初始化方法,使得对象的创建和依赖的解析分开进行。
-
在Spring框架中,三级缓存机制是通过使用注解来解决循环依赖问题的。具体来说,循环依赖问题主要发生在Bean的set赋值这个过程中。为了解决这个问题,Spring框架使用了@Autowired注解来依赖注入Bean。
@Autowired注解可以解决循环依赖问题,因为它会自动地检查三级缓存中是否已经存在目标Bean的实例。如果存在,则直接注入;如果不存在,则等待该Bean被创建后再进行注入。这样,可以确保每个Bean只会被创建一次,避免了循环依赖问题。
此外,三级缓存机制还解决了AOP代理的问题。在Spring框架中,通过AOP的加工,所有bean都会加工成对应的代理类bean。这时三级缓存就是为了解决AOP的问题存在的。在创建代理对象时,AOP会在初始化bean之后,在其后置处理器里创建代理对象。三级缓存解决了半成品bean不是代理类bean的问题,使得AOP可以正常工作。
总之,三级缓存机制和@Autowired注解都是Spring框架中解决循环依赖问题的机制。通过结合使用它们,可以更好地解决循环依赖问题,并确保每个Bean只会被创建一次。
需要注意的是,虽然三级缓存可以解决循环依赖问题,但是在某些情况下可能会导致内存泄漏的问题。因此,在使用三级缓存时需要注意及时清理不再需要的Bean实例。
虽然在架构设计过程中,我们会无意中造成循环依赖的场景,当真正发生相应的问题的时候,我们可以通过步骤二来判断是否真的发生了循环依赖的问题,如果真的是发生了循环依赖问题,那么我们需要根据具体情况分析,看哪一种方式解决问题更加合适,方便。然后结合原理来选择一种合适的解决方案。架构是一门艺术,其中的美用心体会,愿每一个问题的解决,都能给我们带来成就感的同时,也能够解决现实的问题,也成就我们的梦想
相关文章:

springboot的循环依赖问题描述及解决方案
一.了解循环依赖的场景 在Spring Boot中,循环依赖是指两个或多个Bean之间相互依赖,导致它们无法正确地创建和注入。循环依赖可能会导致应用程序无法启动或出现其他异常。 在以下情况下,您可能需要显式设置循环依赖: 两个Bean相…...

当科技遇上神器:用Streamlit定制AI可视化问答界面
Streamlit是一个开源的Python库,利用Streamlit可以快速构建机器学习应用的用户界面。 本文主要探讨如何使用Streamlit构建大模型外部知识检索的AI问答可视化界面。 我们先构建了外部知识检索接口,然后让大模型根据检索返回的结果作为上下文来回答问题。…...

毛泽东思想和中国特色社会主义理论概论平时作业四
毛泽东思想和中国特色社会主义理论概论平时作业四 1.单选题 1.1人民代表大会制度是中国人民当家作主的基本政治制度,是我国的国体。(b) a.正确 b.错误 人民代表大会制度是中国人民当家作主的根本政治制度,是我国的政体。1.2我国的政体是人民民主专政。…...

微信怎么设置自动通过好友申请?
当开展引流获客活动时,员工会在一段时间内频繁收到好友添加的申请,手动同意好友请求费时费力还容易出现漏加的情况,那么微信能自动通过好友请求吗? 如何设置快速自动通过好友申请呢? 当微信号在系统登录,…...

亲测解决Pytorch TypeError: object of type ‘numpy.int64‘ has no len()
这个问题是小虎在初始化自适应平均池化的时候遇到的,解决方法是限制初始化时池化大小的类型。 问题原文 Exception has occurred: TypeError object of type numpy.int64 has no len()File "D:\Complier\LEF\lib\model\segmentation\heads\modules\fgModules…...

前端模拟实现可编辑的表格table插件
在做项目中遇到了一个供货记录的功能,要求用户自己编辑添加删除表格数据,接下来我们就模拟下前端如何实现该功能 <!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8"><meta http-equiv"X-…...

PerfectPixel 插件,前端页面显示优化工具
1.简介 PerfectPixel 插件是一款适用于 Chrome 浏览器的网页前端页面显示优化工具,该插件能够帮助开发人员和标记设计人员在开发时将设计图直接加载至网页中,与已成型的网页进行重叠对比,以规范网页像素精度 作为一款可以优化前端页面显示的…...

mysql迁移data目录(Linux-Centos)
随着时间的推移,mysql的数据量越越大,使用yum默认安装的目录为系统盘 /var/lib/mysql,现重新挂载了一个硬盘,需要做数据目录的迁移到 /mnt/data/。以解决占用系统盘过高情况。 1.强烈建议这种操作。镜像一个一样的Centos系统&…...

linux-等保测评
#查看审计规则 #auditctl -l #添加审计规则 #auditctl -w /etc/passwd -p rwxa(注意:用 auditd 添加审计规则是临时的,立即生效,但是系统重启失效。) #-w path : 指定要监控的路径,上面的命令指定了监控的文…...

一、React基础知识
一、环境安装 第一种:使用原生搭建(可以从国内下载配置镜像、也可以从国外下载) 指令:1.国内下载:(1:npm config set registry https://r.npm.taobao.org// (2:npm install -g create-react-app…...

RocketMQ入门示例-生产者
大家好,本文主要是按照官网的教程把消费者和生产者的示例写下来,开箱即用。 RocketMQ安装 安装请参考官方安装教程: 快速开始 | RocketMQhttps://rocketmq.apache.org/zh/docs/quickStart/01quickstart 本人安装的是最新版本5.x,…...

2023面试知识点三
1、强软弱虚引用 强引用 当内存不足的时候,JVM开始垃圾回收,对于强引用的对象,就算是出现了OOM也不会对该对象进行回收,打死也不回收~! 强引用是我们最常见的普通对象引用,只要还有一个强引用指向一个对象…...

【hcie-cloud】【1】华为云Stack解决方案介绍、华为文档获取方式 【上】
文章目录 华为文档获取方式前言云计算发展背景国家政策、社会发展驱动数字经济开启新时代深化数字化转型提升效率,国家数字主权云进入落地阶段从Cloud-Based到Cloud-Native,两种模式长期并存适合政企智能升级的云华为云Stack,政企智能升级首选…...

JS-类型转换
...

centos7计划任务crontab
当你需要在CentOS 7上定期执行一些任务时,crontab是一个非常有用的工具。它允许你按照预定的时间表自动运行脚本或命令。 1. 查看和编辑crontab 在CentOS 7上,每个用户都有一个自己的crontab文件,用于管理其定时任务。要查看当前用户的cron…...

pycharm 断点调试python Flask
以flask框架为例,其启动命令为 python app.py runserver 后面需要拼接runserver 点击开始断点 参考:https://www.cnblogs.com/bigtreei/p/14742015.html...

Jtti:redis出现太多连接错误怎么解决
Redis出现太多连接错误通常是由于一些常见问题引起的,这些问题可能会导致连接超限、性能下降或服务不可用。以下是一些可能导致Redis连接错误的原因以及如何解决它们的建议: 1. 连接泄漏: 连接泄漏是指在使用完Redis连接后没有正确关闭它们。…...

iOS实现弹簧放大动画
效果图 实现代码 - (void)setUpContraints {CGFloat topImageCentery (SCREEN_HEIGHT - 370 * PLUS_SCALE) / 2;[self.topIconView mas_makeConstraints:^(MASConstraintMaker *make) {make.centerX.mas_equalTo(0);make.centerY.equalTo(self.view.mas_top).with.offset(t…...

③ 软件工程CMM、CMMI模型【软考中级-软件设计师 考点】
个人简介:Java领域新星创作者;阿里云技术博主、星级博主、专家博主;正在Java学习的路上摸爬滚打,记录学习的过程~ 个人主页:.29.的博客 学习社区:进去逛一逛~ ③ 软件工程CMM、CMMI模型【软考中级-软件设计…...

JumpServer开源堡垒机与万里安全数据库完成兼容性认证
近日,中国领先的开源软件提供商FIT2CLOUD飞致云宣布,JumpServer开源堡垒机已经与万里安全数据库软件GreatDB完成兼容性认证。针对产品的功能、性能、兼容性方面,经过双方共同测试,万里安全数据库软件(简称:…...

蓝桥杯每日一题2023.10.31
题目描述 全球变暖 - 蓝桥云课 (lanqiao.cn) 题目分析 果然有关连通块类的问题使用dfs都较为好写~~ 我们可以通过判断连通块的代码来加上部分条件算出被完全淹没的岛屿个数 在岛屿中如果有为"#"的a[i][j]上下左右全部是"#"则说明此岛屿一定不会被完全…...

【兔子王赠书第5期】ChatGPT速学通:文案写作+PPT制作+数据分析+知识学习与变现
文章目录 前言ChatGPT推荐图书作者简介内容简介推荐理由 粉丝福利尾声 前言 程序员如果有一天代码写不动了,还能干什么? 一位 80 后女程序员“兰猫”给出了她的答案——转型 AI 写手。兰猫从事程序员工作十余年,在繁重的工作压力下…...

selenium爬虫——以爬取澎湃新闻某搜索结果为例
文章目录 selenium爬虫——以爬取澎湃新闻某搜索结果为例前言需要导入的包需要避雷的点webdriver的版本要与浏览器一致如果使用爬虫打开了新网页,要记得跳转XPath和selector都可以直接复制爬取多网页时记得try打入word时调整字体的问题 完整程序扩展爬取效果 seleni…...

基于GEE云平台一种快速修复Landsat影像条带色差的方法
这是之前关于去除遥感影像条带的另一篇文章,因为出版商推迟了一年发布,所以让大家久等了。这篇文章的主要目的是对Landsat系列卫星因为条带拼接或者镶嵌产生的条带来进行的一种在线修复方式。 原文连接 一种快速修复Landsat影像条带色差的方法 题目&a…...

云栖大会 | 科技改变生活,移远通信实力引领智能未来
科技对生活的改变体现在出行方式、娱乐方式、支付方式等多个方面,已经融入了我们的日常生活,为我们带来了便捷、高效、舒适的体验。 10月31日—11月2日,云栖大会在杭州盛大召开。本次大会以“计算,为了无法计算的价值”为主题&…...

FMC子卡解决方案:FMC214-基于FMC兼容1.8V IO的Full Camera Link 输出子卡
FMC214-基于FMC兼容1.8V IO的Full Camera Link 输出子卡 一、板卡概述 基于FMC兼容1.8V IO的Full Camera Link 输出子卡支持Base、Middle、Full Camera link信号输出,兼容1.8V、2.5V、3.3V IO FPGA信号输出。适配xilinx不同型号开发板和公司内部各FMC载板。北…...

stm32 模拟spi
目录 简介 spi物理层 连接方式 框图 协议层: 数据处理 传输模式 模式0 起始和停止信号 发送和接收数据 模式1 模式2 模式3 总结 简介 spi物理层 SPI( Serial Peripheral Interface, 串行外设接口)是一种全双工同步…...

小程序https证书
小程序通常需要与服务器进行数据交换,包括用户登录信息、个人资料、支付信息等敏感数据。如果不使用HTTPS,这些数据将以明文的方式在网络上传输,容易被恶意攻击者截获和窃取。HTTPS通过数据加密来解决这个问题,确保数据在传输过程…...

《python深度学习》笔记(二十):神经网络的解释方法之CAM、Grad-CAM、Grad-CAM++、LayerCAM
原理优点缺点GAP将多维特征映射降维为一个固定长度的特征向量①减少了模型的参数量;②保留更多的空间位置信息;③可并行计算,计算效率高;④具有一定程度的不变性①可能导致信息的损失;②忽略不同尺度的空间信息CAM利用…...

Python中文件copy模块shutil
高级的 文件、文件夹、压缩包 处理模块 shutil.copyfileobj(fsrc, fdst[, length])将文件内容拷贝到另一个文件中 import shutil shutil.copyfileobj(open(old.xml,r), open(new.xml, w)) shutil.copyfile(src, dst)拷贝文件 shutil.copyfile(f1.log, f2.log) #目标文件无需…...