springboot如何解决和避免循环依赖问题
- 发表于:2022-10-27 14:55
- 阅读(1016)
(1)什么情况会发生循环依赖?
循环依赖其实就是一个类或多个类中直接或间接的相互依赖注入,他们之间的依赖关系就像一个闭环一样,构成环形调用,但当存在循环依赖时,Spring将无法决定先创建哪个bean,所以就导致项目启动失败。
常见会发生循环依赖主要是以下几种情况:
自己依赖自己的直接依赖
两个类之间的直接依赖
多个类之间的间接依赖
报错如下:
1、自己依赖自己的直接依赖:
CircularDependencyA --引入依赖--> CircularDependencyA
例如,现在我有一个类叫:CircularDependencyA,我在CircularDependencyA这个类里面引入CircularDependencyA。
CircularDependencyA:
package com.demo.service;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
/**
* @author guppies
* @since 2022-10-27
*/
@Service
@AllArgsConstructor
public class CircularDependencyA {
private final CircularDependencyA circularDependencyA;
}
启动项目就会发现报如下错误,并且循环依赖是无法正常启动项目的:
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-10-27 14:48:00.116 ERROR [] [main] o.s.b.diagnostics.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| circularDependencyA defined in file [/Users/XXX/demo/target/classes/com/demo/service/CircularDependencyA.class]
└─────┘
Disconnected from the target VM, address: '127.0.0.1:56247', transport: 'socket'
Process finished with exit code 1
不过这种情况很少发生,我们很少会在service类中引入自身。
2、两个类之间的直接依赖:
CircularDependencyA --引入依赖--> CircularDependencyB --引入依赖--> CircularDependencyA
例如,现在我有一个类叫:CircularDependencyA,我在CircularDependencyA这个类里面引入CircularDependencyB,然后在CircularDependencyB中又引入CircularDependencyA。
CircularDependencyA:
package com.demo.service;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author guppies
* @since 2022-10-27
*/
@Service
@AllArgsConstructor
public class CircularDependencyA {
//private final CircularDependencyA circularDependencyA;
//@Autowired
//private CircularDependencyA circularDependencyA;
private final CircularDependencyB circularDependencyB;
}
CircularDependencyB:
package com.demo.service;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
/**
* @author guppies
* @since 2022-10-27
*/
@Service
@AllArgsConstructor
public class CircularDependencyB {
private final CircularDependencyA circularDependencyA;
}
启动项目就会发现报如下错误,并且循环依赖是无法正常启动项目的:
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-10-27 15:07:32.051 ERROR [] [main] o.s.b.diagnostics.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| circularDependencyA defined in file [/Users/XXX/demo/target/classes/com/demo/service/CircularDependencyA.class]
↑ ↓
| circularDependencyB defined in file [/Users/XXX/demo/target/classes/com/demo/service/CircularDependencyB.class]
└─────┘
Disconnected from the target VM, address: '127.0.0.1:56862', transport: 'socket'
Process finished with exit code 1
这种情况应该是普遍会遇到的。
3、多个类之间的间接依赖:
CircularDependencyA --引入依赖--> CircularDependencyB --引入依赖--> CircularDependencyC --引入依赖--> CircularDependencyD --引入依赖--> CircularDependencyA 或 CircularDependencyB 或 CircularDependencyC
例如,现在我有一个类叫:CircularDependencyA,我在CircularDependencyA这个类里面引入CircularDependencyB,然后在CircularDependencyB中引入CircularDependencyC,然后在CircularDependencyC中引入CircularDependencyD,然后在CircularDependencyD中引入CircularDependencyA或CircularDependencyB或CircularDependencyC。
CircularDependencyA:
package com.demo.service;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author guppies
* @since 2022-10-27
*/
@Service
@AllArgsConstructor
public class CircularDependencyA {
//会报循环依赖错误
//private final CircularDependencyA circularDependencyA;
//@Autowired
//private CircularDependencyA circularDependencyA;
private final CircularDependencyB circularDependencyB;
//@Autowired
//private CircularDependencyB circularDependencyB;
}
CircularDependencyB:
package com.demo.service;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author guppies
* @since 2022-10-27
*/
@Service
@AllArgsConstructor
public class CircularDependencyB {
//会报循环依赖错误
//private final CircularDependencyA circularDependencyA;
//@Autowired
//private CircularDependencyA circularDependencyA;
private final CircularDependencyC circularDependencyC;
}
CircularDependencyC:
package com.demo.service;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
/**
* @author guppies
* @since 2022-10-27
*/
@Service
@AllArgsConstructor
public class CircularDependencyC {
//会报循环依赖错误
//private final CircularDependencyA circularDependencyA;
//会报循环依赖错误
//private final CircularDependencyB circularDependencyB;
private final CircularDependencyD circularDependencyD;
}
CircularDependencyD:
package com.demo.service;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
/**
* @author guppies
* @since 2022-10-27
*/
@Service
@AllArgsConstructor
public class CircularDependencyD {
//会报循环依赖错误
//private final CircularDependencyA circularDependencyA;
//会报循环依赖错误
//private final CircularDependencyB circularDependencyB;
//会报循环依赖错误
private final CircularDependencyC circularDependencyC;
}
启动项目就会发现报如下错误,并且循环依赖是无法正常启动项目的:
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-10-27 15:22:54.272 ERROR [] [main] o.s.b.diagnostics.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
circularDependencyA defined in file [/Users/XXX/demo/target/classes/com/demo/service/CircularDependencyA.class]
↓
circularDependencyB defined in file [/Users/XXX/demo/target/classes/com/demo/service/CircularDependencyB.class]
┌─────┐
| circularDependencyC defined in file [/Users/XXX/demo/target/classes/com/demo/service/CircularDependencyC.class]
↑ ↓
| circularDependencyD defined in file [/Users/XXX/demo/target/classes/com/demo/service/CircularDependencyD.class]
└─────┘
Disconnected from the target VM, address: '127.0.0.1:57485', transport: 'socket'
Process finished with exit code 1
这种情况应该也是经常会遇到的。
实验证明,多个类间接依赖,后面的类,不管是中间节点类,还是末尾节点类,只要引入依赖了前面节点的某一个类,都会报循环依赖错误。
(2)怎么解决和避免循环依赖?
1、我们如果是使用构造方法的方式引入依赖的话,就容易导致循环依赖,改成使用 @Autowired
的方式引入应该就可以避免启动的时候报循环依赖错误了。
例如:
1、针对上面第一种情况可以做如下改进:
CircularDependencyA:
package com.demo.service;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author guppies
* @since 2022-10-27
*/
@Service
//@AllArgsConstructor
public class CircularDependencyA {
//private final CircularDependencyA circularDependencyA;
@Autowired
private CircularDependencyA circularDependencyA;
}
这样项目就可以正常启动了。
2、针对上面第二种情况,我们可以改成如下方式引入:
CircularDependencyA:
package com.demo.service;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author guppies
* @since 2022-10-27
*/
@Service
//@AllArgsConstructor
public class CircularDependencyA {
//private final CircularDependencyA circularDependencyA;
//@Autowired
//private CircularDependencyA circularDependencyA;
//private final CircularDependencyB circularDependencyB;
@Autowired
private CircularDependencyB circularDependencyB;
}
CircularDependencyB:
package com.demo.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author guppies
* @since 2022-10-27
*/
@Service
//@AllArgsConstructor
public class CircularDependencyB {
//private final CircularDependencyA circularDependencyA;
@Autowired
private CircularDependencyA circularDependencyA;
}
这样项目就可以正常启动了。
针对第三种情况也是类似的,只要使用注解的方式引入依赖,而不是使用构造方式的方式,就可以避免报错了。
2、使用 @Lazy 注解,这是针对已有代码最简单的解决方案,指定某一个类延迟加载或懒加载,在注入依赖的时候,先注入代理对象,当首次使用时再创建对象完成注入。
package com.demo.service;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
/**
* @author guppies
* @since 2022-10-27
*/
@Service
//@AllArgsConstructor
public class CircularDependencyD {
//会报循环依赖错误
//private final CircularDependencyA circularDependencyA;
//会报循环依赖错误
//private final CircularDependencyB circularDependencyB;
//会报循环依赖错误
//private final CircularDependencyC circularDependencyC;
private final CircularDependencyC circularDependencyC;
//private CircularDependencyC circularDependencyC;
//会报循环依赖错误
/*public CircularDependencyD(CircularDependencyC circularDependencyC) {
this.circularDependencyC = circularDependencyC;
}*/
//可以避免循环依赖错误
public CircularDependencyD(@Lazy CircularDependencyC circularDependencyC) {
this.circularDependencyC = circularDependencyC;
}
//会报循环依赖错误
/*@Autowired
public void setCircularDependencyC(CircularDependencyC circularDependencyC) {
this.circularDependencyC = circularDependencyC;
}
public CircularDependencyC getCircularDependencyC() {
return circularDependencyC;
}*/
}
3、重新设计项目结构。
(5)总结
针对发生循环依赖的情况,我们可以使用以上几种方式来解决或避免,如果项目处于开始阶段,建议重新设计好项目结构,通过合理的项目结构来避免循环依赖错误,如果项目已经比较庞大,就优先使用以上其中一种方式来注入来解决和避免循环依赖。
相关推荐
-
mac安装subversion,并使用svn命令检出服务器上的代码库项目
mac安装svn只要通过Homebrew安装即可,不需要下载额外的安装包手动安装,Homebrew类似一个软件库,我们可以通过brew命令实现一键下载并安装我们所需要的常用软件。
-
Java如何获取泛型类T的Class
我们平时在封装接口或抽象类的时候经常会用到Java的泛型,经常会在传入一个泛型类T,然后封装一些抽象的方法,泛型的好处就是在编译的时候检查类型安全,并且所有的强制类型转换都是隐式和自动的,这样可以提高代码的通用性。但是我们有时候需要获取泛型类的Class,那可以如何获取到呢?
-
springboot项目事务报错:Transaction synchronization is not active
这几天在使用spring声明式事务的时候突然报了一个错误:Transaction synchronization is not active,之前使用的都是好好的,为什么这次就不行了呢?不就是加一个 @Transactional 而已嘛???
-
mysql应该如何在where语句中添加if语句进行条件判断?where if 语句应该如何使用
我们在平时的项目开发中,有时候会遇到复杂一点的需求,需要我们手动编写复杂的SQL语句,并且有时候需要根据每条表记录的实际情况进行判断,根据每条记录动态添加不同的where条件,这个时候我们就可以在where语句中使用if语句进行条件判断,那么where if应该如何正确使用呢?
-
关于websocket多节点分布式问题的解决方案
websocket是一种在单个TCP连接上进行双全工通信的协议,使用websocket,我们可以实现服务端主动向各个订阅消息通道的客户端推送消息。这点比传统的http轮询请求要更好一点,避免一些无用的请求,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
-
mac如何把文件压缩成tar、zip以及如何解压tar、zip?
有时候我们需要把文件压缩成一个tar文件或zip文件,发送给别人,那么在macos系统应该如何压缩和解压缩呢?
-
openjdk和jdk有什么区别,应该如何选择?
我们一开始学习java的时候,安装的都是从sun官网或oracle官网下载的jdk安装包,但其实还有另外一个来源可以获取到jdk安装包,那就是openjdk,它和jdk基本一样,推荐使用openjdk。
-
如果git仓库发生变更,IDEA如何直接修改git远程仓库地址?
有时候我们整理远程仓库代码的时候,会修改远程仓库的名称,或者所属分组,这个时候在IDEA由于还是使用原先拉取的旧仓库地址,导致本地代码会提交不了,也更新不了远程最新代码,那么这个时候要如何修改IDEA当前的git远程仓库地址呢?如何无缝修改,修改完之后就能和原来一样更新提交,并且以前的提交记录也保留呢?
-
微信公众号自定义菜单报错:no permission to use weapp in menu rid:xxxxxxx
昨晚公司系统添加微信公众号菜单突然报错:{"errcode":45064,"errmsg":"no permission to use weapp in menu rid: 60311f70-0736ff08-29143906"}
-
Java如何使用stream流对List列表数据进行自定义排序
我们一般做排序功能都是通过在mysql数据库中的表中定义好排序字段,然后使用升序或降序来进行排序,复杂一点的话就配合多个排序字段进行排序,但是如果碰到那种无法使用表的字段进行排序的情况,我们需要先从数据库中取出列表数据,然后再通过业务代码对列表进行排序,这个时候我们就可以使用redis或Java的stream流。
-
微信企业付款到零钱报错:此请求可能存在风险,已被微信拦截
具体错误信息:com.github.binarywang.wxpay.exception.WxPayException: 返回代码:[SUCCESS],返回信息:[支付失败],结果代码:[FAIL],错误代码:[NO_AUTH],错误详情:[此请求可能存在风险,已被微信拦截。]
-
springboot项目使用@Transactional注解如何避免长事务问题
在springboot项目中,我们开启事务是非常简单的,使用注解的方式就是在需要开启事务的方法上添加@Transactional,这样就可以实现这个方法里面的所有操作和调用方法的操作都绑定在一个事务上面,要么全部一起执行成功,要么全部一起执行失败,如果其中有某个地方抛了异常,则整个方法涉及的事务操作都会回滚,但是如果随意滥用@Transactional,又有可能引发长事务问题,导致数据库死锁、数据库连接池占满等问题。
-
css实现“展开阅读全文”功能
最近发现很多博客网站,资讯网站喜欢把资讯博文,内容等这些大文本的信息在页面显示的时候都会有个“展开阅读全文”的按钮,点击这个按钮即可展开显示所有的内容,不然一开始就显示那么长的篇幅相对来说既不美观,又对用户体验不好。现在就让我来仿照这类网站实现一个“展开阅读全文”功能。这里主要用到的前端技术是html+jquery+css,只做展开功能,没做收起功能(收起功能没必要吧,谁会去收起呀???)。
-
关于编程中面向对象的理解,什么是面向对象
面向对象设计相对于结构化程序设计可以说是一种更优秀的程序设计方法。它的基本思想是使用类、对象、继承、封装、消息等基本概念进行程序设计。它是从现实世界中客观存在的事物(即对象)出发来构造软件系统,并在软件系统构造中尽可能运用人类的自然思维方式,强调直接以现实世界中的事物(即对象)为中心来思考,认识问题。
-
我的linux操作命令总结,记录常用linux操作命令
平常本地开发项目使用的系统基本都是window系统,而且都是图形化操作,非常方便,window也是越做越好了,项目部署到生产环境一般都是选择linux系统(当然window server系列也可以),而linux一般则选择centOS居多,这里记录一下linux常用命令,以免老是过几天就忘了,后续不断补充。