请说明 Spring 框架中循环依赖问题的解决方案,包括其核心机制、适用条件以及相关限制。
考察说明
考查对 Spring 循环依赖解决机制及限制的理解。
回答思路
- 【回答框架 1】Spring 通过三级缓存解决单例 Bean 的 setter 注入循环依赖。一级缓存存放完整 Bean,二级缓存存放早期暴露的 Bean,三级缓存存放对象工厂。创建 A 时,A 的实例化后将其工厂放入三级缓存,填充属性时发现依赖 B,创建 B 并填入 A 的早期引用,B 完成后 A 再注入 B 并完成初始化。
- 【回答框架 2】三级缓存的关键在于提前暴露对象引用,使得循环依赖的 Bean 可以获取未完全初始化的实例。仅当存在循环依赖时,才会用到二级和三级缓存;正常情况下三级缓存中的工厂不会被调用。
- 【回答框架 3】该机制只适用于单例作用域和 setter 注入(或字段注入),不适用于构造器注入,因为构造器注入在实例化前就需要依赖,导致无法提前暴露。原型作用域 Bean 不缓存,也无法解决循环依赖,会直接抛出异常。
- 【回答框架 4】Spring 默认允许单例 setter 注入的循环依赖,但可以通过配置关闭。此外,循环依赖可能掩盖设计问题,例如职责过重或过度耦合,官方建议优先通过重构避免。
- 【关键点 1】三级缓存分别存储完整 Bean、早期 Bean 引用和 Bean 工厂。
- 【关键点 2】核心机制是提前暴露对象引用,使循环依赖的 Bean 能获取未初始化完成的实例。
- 【关键点 3】仅适用于单例作用域和 setter/字段注入,不适用于构造器注入和原型作用域。
- 【关键点 4】循环依赖解决不保证 Bean 完整初始化,注入的可能是早期引用。
- 【易错点 1】误认为构造器注入也能解决循环依赖,实际会报错。
- 【易错点 2】忽略循环依赖可能导致的隐藏设计问题,如循环依赖掩盖高耦合。
- 【易错点 3】混淆三级缓存与性能优化,三级缓存主要用于解决循环依赖,并非性能优化手段。