Spring三级缓存仅适用于单例Bean,因其具备全局唯一性和可控生命周期,可提前暴露半成品实例解决循环依赖;原型Bean每次创建新实例且无状态共享,构造器注入则因依赖必须先于自身构造导致死锁,二者均无法使用该机制。

因为只有单例 Bean 才能被缓存复用,Spring 的三级缓存机制才得以生效;非单例(如 prototype)每次调用 getBean() 都会新建实例,无法共享中间状态,自然无法打破循环。
单例是缓存复用的前提
Spring 解决循环依赖的核心不是“绕过”依赖,而是“提前暴露一个可用的引用”。这个引用必须是同一个对象——否则 A 注入的 B 和 B 注入的 A 就不是彼此看到的同一份实例,逻辑就乱了。单例作用域保证了:
• 容器中每个类只有一个实例;
• 该实例一旦创建,后续所有获取都返回它;
• 构造完成但属性未填充的“早期对象”,也能安全地被另一个正在创建的 Bean 引用。
原型 Bean 为什么不行
prototype 每次 getBean() 都 new 一个新对象,没有全局唯一性:
• A 创建时需要 B → 新建 B;
• B 创建时需要 C → 新建 C;
• C 创建时又需要 A → 再新建 A;
• 新建的 A 又要新建 B……无限递归。
Spring 在检测到 prototype Bean 正在创建中时,直接抛 BeanCurrentlyInCreationException 中断流程,不尝试解决。
构造器注入为何也不行(即使是单例)
构造器注入要求“先有依赖,再造自己”,而依赖本身又在构造中,形成死锁:
• 要创建 A,必须先拿到 B;
• 要创建 B,必须先拿到 C;
• 要创建 C,必须先拿到 A;
• A 还没造出来,C 就卡住,整个链路无法启动。
此时连“提前暴露”都无从谈起——对象连构造都没开始,根本不存在可暴露的引用。
立即学习“Java免费学习笔记(深入)”;
关键不在“单例”本身,而在“可预测的生命周期”
单例 Bean 的生命周期由容器严格控制:实例化 → 提前暴露 → 属性填充 → 初始化。这个确定性让 Spring 能在恰当的时机把“半成品”放进三级缓存(singletonFactories),供其他 Bean 安全引用。而 prototype 或构造器注入破坏了这种可控节奏,缓存机制就失去了立足基础。


















