Spring的循环依赖

循环依赖:一个对象或多个对象之间存在直接或间接的依赖关系,这种关系构成了一种循环调用。

  1. 自己依赖自己的直接依赖
  2. 两个对象之间的直接依赖
  3. 多个对象之间的间接依赖

循环依赖的N种场景

  1. 单例的setter注入(能解决)
  2. 多例的setter注入(不能解决)
  3. 构造器注入(不能解决)
  4. 单例的代理对象setter注入(有可能解决)
  5. DependsOn循环依赖(不能解决)

第一种情况,直接通过@Autowired直接注入的循环依赖Spring利用内部的三级缓存已解决
spring内部有三级缓存:DefaultSingletonBeanRegistry

  1. singletonObjects 一级缓存,用于保存实例化、注入、初始化完成的bean实例
  2. earlySingletonObjects 二级缓存,用于保存实例化完成的bean实例(此时还未填充属性)
  3. singletonFactories 三级缓存,用于保存bean创建工厂,以便于后面扩展有机会创建代理对象

spring解决循环依赖的方式
bean1=>一级缓存中获取不到,创建实例=>提前暴露,添加到三级缓存=>依赖注入bean2=>一级缓存中获取不到,创建实例=>提前暴露,添加到三级缓存
=>依赖注入bean1=>三级缓存中获取到实例,并添加到二级缓存=>bean2的依赖注入成功,bean2初始化成功,bean2添加到一级缓存
=>bean1的依赖注入成功,bean1初始化成功,bean1添加到一级缓存=>结束

第二种情况,多例的注入,使用@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)将bean声明为多例的类
此时程序能够正常启动,非单例不会被提前初始化(非抽象、单例 并且非懒加载的类才能被提前初始bean)
这种循环依赖问题是无法解决的,因为它没有用缓存,每次都会生成一个新对象。

第三种情况,构造器注入
此种情况在创建实例的时候便进行了对应依赖的创建,无法利用到缓存,也无法解决循环依赖

第四种情况,单例的代理对象setter注入,@Async注解的场景,会通过AOP自动生成代理对象
如图一的情形,bean初始化完成之后,后面还有一步去检查:第二级缓存 和 原始对象 是否相等,如不相等便抛出循环依赖异常
默认情况下,spring是按照文件完整路径递归查找的,按路径+文件名排序,排在前面的先加载,由于加载顺序的不一致,有可能不会出现循环依赖
对于A中注入B的方式为setter方法,B中注入A的方式为构造器的循环依赖:如果A先加载则可以解决循环依赖,B先加载则不能;依据自然排序的加载顺序加载

第五种情况,使用@DependsOn注解会出现循环依赖
AbstractBeanFactory类的doGetBean会检查dependsOn的实例有没有循环依赖,如果有循环依赖则抛异常

解决循环依赖的方法:

  1. 使用@Lazy注解,延迟加载(构造器循环依赖)
  2. 使用@DependsOn注解,指定加载先后关系
  3. 修改文件名称,改变循环依赖的加载顺序(单例的代理对象setter注入的循环依赖)
posted @   Abserver  阅读(2064)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示