Spring spring

Spring中文网站 > Spring Batch > Spring如何解决循环依赖 Spring默认只处理单例Bean的循环依赖吗
Spring如何解决循环依赖 Spring默认只处理单例Bean的循环依赖吗
发布时间:2026/04/21 11:16:52

品牌型号:联想拯救者R7000

系统:Windows 10专业版

软件版本:Spring Boot 4.0.2

我们在使用Spring框架开发时,可能会遇到循环依赖的问题,一般是在复杂业务场景中,多个Service之间相互引用,就会导致循环依赖问题。比如订单服务需要调用用户服务获取用户信息,而用户服务又需要调用订单服务查询用户相关订单,如果不主动处理循环依赖问题,会导致启动报错。本文将为大家介绍Spring如何解决循环依赖,Spring默认只处理单例Bean的循环依赖吗的相关内容。

一、Spring如何解决循环依赖

什么是循环依赖?启动Spring Boot项目时,如果两个或多个Bean之间形成了“相互依赖”,例如下图,classService和studentService之间相互引用了,就会出现下图所示的日志,并且项目会启动失败。

循环依赖
图1:循环依赖

Spring解决循环依赖的机制是【三级缓存】,通过分层缓存提前使用未完全初始化的Bean,解决循环依赖问题,该机制仅适用于单例Bean的字段注入场景,也是工作中常遇到的循环依赖类型。

Spring三级缓存
图2:Spring三级缓存

在日常开发中,我们可能会遇到这样的场景:Service需要注入UserService来校验下单用户是否正常,而UserService需要注入Service来查询用户的历史订单记录,两者就会形成循环依赖。此时Spring通过三级缓存来处理,下面我就为大家拆解三级缓存的工作流程。

1、三级缓存

存储Bean的工厂对象(ObjectFactory),用于生成未完全初始化的Bean早期引用。当Spring实例化Service后,会将其包装为ObjectFactory存入三级缓存,其他Bean引用它时,能够直接使用早期Bean,避免死循环。

三级缓存
图3:三级缓存

2、二级缓存

存储提前暴露的未完全初始化Bean,也就是已实例化,未完成属性注入和初始化。当Service需要注入UserService时,Spring会去创建UserService,而UserService又需要注入Service,此时Spring会从三级缓存中获取Service的工厂对象,生成其早期引用,存入二级缓存,同时移除三级缓存中的工厂对象,确保后续其他Bean依赖时可直接获取。

二级缓存
图4:二级缓存

3、一级缓存

存储初始化完成的单例Bean,业务能直接使用。当UserService完成属性注入和初始化后,会被存入一级缓存,此时Service可以从一级缓存中获取UserService的完整Bean,完成自身的属性注入和初始化,最终也存入一级缓存,循环依赖得以解决。

一级缓存
图5:一级缓存

如果循环依赖是通过构造函数注入实现的,即使是单例Bean,Spring也无法自动解决,会直接抛出异常。工作中遇到这种情况,可通过【@Lazy】注解延迟注入,或改为字段注入,尽可能不要构造函数注入。

延迟加载
图6:延迟加载

二、Spring默认只处理单例Bean的循环依赖吗

Spring默认只处理单例Bean的循环依赖,对于非单例Bean的循环依赖,Spring无法自动处理,会直接抛出循环依赖异常。下面我就为大家介绍一下单例和非单例Bean的处理差异。

1、单例Bean是Spring默认的Bean作用域,容器中仅存在一个实例,所以三级缓存才能生效。因为单例Bean是唯一的,Spring可以通过缓存提前暴露其早期引用,后续依赖时直接复用,无需重复创建,从而打破循环依赖闭环。

默认单例Bean
图7:默认单例Bean

2、对于原型Bean,Spring默认不处理其循环依赖,原因很简单:原型Bean的作用域是每次获取都会创建一个新的实例,不会存入三级缓存。比如我们在Service和UserService上添加【@Scope("prototype")】注解,使其成为原型Bean,此时Spring创建Service时需要注入UserService,会创建一个新的UserService实例,而UserService又需要注入Service,又会创建一个新的Service实例,如此反复,形成死循环。

原型Bean
图8:原型Bean

工作中遇到原型Bean循环依赖的场景,常见的解决方案有两种:一是将原型Bean改为单例Bean,借助Spring的三级缓存自动处理,二是通过@Lazy注解延迟注入。比如在原型Bean的依赖字段上添加@Lazy注解,Spring会生成一个代理对象注入,当第一次使用该依赖时才会真正创建实例,打破循环。

三、总结

以上就是Spring如何解决循环依赖,Spring默认只处理单例Bean的循环依赖吗的相关内容。本文为大家介绍了Spring三级缓存解决循环依赖的问题,这也是一个高频面试考点,文中为大家解释了源码,建议了解这些源码即可。Spring默认只能处理单例Bean的循环依赖,使用原型Bean时,无法自动处理,可以参考上文步骤手动处理,希望对你有所帮助。

180 1563 6924