什么是循环依赖
循环依赖(circular dependency) 是指两个或多个 bean 相互直接或间接依赖,导致容器无法正常初始化这些 bean。
@service public class servicea { @autowired private serviceb serviceb; // servicea 依赖 serviceb } @service public class serviceb { @autowired private servicea servicea; // serviceb 依赖 servicea }
spring boot 基于 spring 框架,其循环依赖的处理机制与 spring 一致,但在 spring boot 2.6+ 版本中默认禁止了循环依赖(通过 spring.main.allow-circular-references=false
)。
产生循环依赖的原因
1.构造函数注入循环依赖:
@service public class servicea { private final serviceb serviceb; public servicea(serviceb serviceb) { // 构造函数注入 this.serviceb = serviceb; } } @service public class serviceb { private final servicea servicea; public serviceb(servicea servicea) { // 构造函数注入 this.servicea = servicea; } }
直接报错:构造函数注入的循环依赖无法解决,容器启动时抛出
beancurrentlyincreationexception
。
2.setter/field 注入循环依赖:
在spring中使用@autowired注解标签进行自动注入,如果不加以处理,会出现循环依赖问题 。
怎么解决循环依赖
在springboot2.5以前可以通过三级缓存解决单例 bean 的循环依赖问题。
缓存名称 | 职责 |
---|---|
singletonobjects | 存放完全初始化好的 bean(一级缓存) |
earlysingletonobjects | 存放提前暴露的早期 bean(二级缓存) |
singletonfactories | 存放 bean 的工厂对象(三级缓存) |
以最初的servicea与serviceb为例,
创建
servicea
,通过工厂将其半成品引用存入三级缓存。servicea
注入serviceb
,触发serviceb
的创建。创建
serviceb
,同样将其半成品引用存入三级缓存。serviceb
注入servicea
时,从三级缓存中获取servicea
的早期引用,完成serviceb
的初始化。serviceb
初始化完成后,servicea
完成依赖注入,最终初始化。
出现循环依赖之后的几个解决思路:
1.避免循环依赖(推荐)
重构代码:将公共逻辑抽离到第三个 bean 中。
使用接口或抽象类:通过面向接口编程解耦具体实现。
2. 允许循环依赖(临时方案)
在 application.properties
中显式允许循环依赖:
# spring boot 2.6+ 需要手动开启 spring.main.allow-circular-references=true
这种只适用于springboot版本在2.6以上的循环依赖被禁止的情形。
3. 使用 @lazy
延迟加载
在其中一个依赖上添加 @lazy
,延迟注入 bean 的初始化:
@service public class servicea { @lazy @autowired private serviceb serviceb; // 延迟初始化 serviceb }
4. 调整注入方式
优先使用 setter/field 注入:避免构造函数注入导致的不可解循环依赖。
@service public class servicea { private serviceb serviceb; @autowired public void setserviceb(serviceb serviceb) { // setter 注入 this.serviceb = serviceb; } }
使用setter注入
循环依赖的局限性
构造函数注入无法解决循环依赖:spring 容器在创建 bean 时需先完成构造函数调用,此时依赖的 bean 尚未初始化。
原型(prototype)作用域的 bean:spring 不管理原型 bean 的完整生命周期,无法解决其循环依赖。
aop 代理问题:如果 bean 被 aop 代理(如
@async
、@transactional
),可能导致循环依赖解决失败。
总结
spring boot 的循环依赖本质是 spring 框架的机制问题,解决核心在于:
理解三级缓存的工作原理。
优先通过代码设计避免循环依赖。
必要时合理使用
@lazy
或调整注入方式。
尽可能在设计之初就避免循环依赖
到此这篇关于springboot循环依赖的原因及解决的文章就介绍到这了,更多相关springboot循环依赖内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论