如何复现Spring字段注入(Field Injection)场景下的循环依赖错误
问题答复
1. 字段注入场景下循环依赖错误的触发时机
首先纠正一个认知偏差:Spring默认配置下,单例Bean的字段/Setter注入循环依赖不会触发报错,这是Spring内置的三级缓存机制专门做了兼容,会提前暴露未完成初始化的Bean引用来解决循环依赖问题。
只有不符合三级缓存解决条件的场景,才会触发循环依赖异常,触发时机固定是Spring容器启动阶段(Bean实例化、属性填充、初始化的流程中),不会等到业务代码实际调用的运行阶段才抛出错误。你之前写的例子没有报错,就是因为符合三级缓存的解决条件。
2. 字段注入复现循环依赖错误的方法
你可以任选以下任意一种方式修改代码,即可触发字段注入的循环依赖报错:
- 方式一:关闭Spring循环依赖支持
Spring Boot 2.6及以上版本默认已经禁用循环依赖,低版本可以在配置文件中添加配置:
重启项目启动阶段直接抛出循环依赖异常。spring.main.allow-circular-references=false - 方式二:修改Bean作用域为原型
给类A、类B都添加@Scope("prototype")注解,原型Bean每次请求都会创建新实例,Spring无法通过缓存解决循环依赖,启动直接报错。 - 方式三:添加异步注解破坏代理逻辑
给类A或类B的任意方法添加@Async注解(需要先开启异步支持@EnableAsync),此时生成的代理对象无法被三级缓存处理,启动就会抛出循环依赖异常。
3. 字段注入替代构造器注入的风险提示
你只关注到循环依赖的风险,实际还有几个落地层面的实际问题需要考虑:
- 不可变性缺失:构造器注入可以将依赖声明为
final类型,避免业务代码意外修改依赖实例,字段注入无法声明为final,存在被篡改的风险。 - 单元测试成本更高:构造器注入的类做单元测试时,直接new对象传入Mock的依赖即可,不需要依赖Spring上下文、不需要用反射注入字段,测试效率更高。
- 架构坏味道被隐藏:构造器注入如果存在循环依赖,启动阶段直接报错,强制你优化架构解除耦合;字段注入虽然Spring帮你兼容了循环依赖,但本质是把架构耦合的问题藏到了底层,后续业务迭代修改时很容易出现意料之外的问题。
如果你的团队已经明确接受以上风险,再考虑推广字段注入。
内容的提问来源于stack exchange,提问作者Aditya Rewari
相关产品推荐
相关产品推荐

