直接回答:Spring 通过三级缓存解决单例 Bean 的 setter/字段注入循环依赖,本质是"提前暴露一个尚未完成初始化的对象引用"。构造器注入的循环依赖和 prototype 的循环依赖无法解决,启动即报错。
三级缓存:
- 一级
singletonObjects:完全初始化好的成品 Bean; - 二级
earlySingletonObjects:提前曝光的半成品 Bean; - 三级
singletonFactories:能产出早期引用的ObjectFactory。
流程(A 依赖 B,B 依赖 A):A 先实例化(仅构造完成),把一个 ObjectFactory 放入三级缓存;A 填充属性时发现需要 B → B 开始创建,填充时又需要 A → 此时从三级缓存取到 A 的工厂,调用 getObject() 得到 A 的早期引用放入二级缓存并注入给 B;B 初始化完成进入一级缓存;回到 A 继续注入 B,A 完成。
为什么必须三级而不是二级:关键在 AOP。若 A 需要被代理,getObject() 里通过 SmartInstantiationAwareBeanPostProcessor 提前创建代理对象——工厂让"返回原始对象还是代理对象"这一决策延迟到真正被依赖时发生,保证所有地方注入的引用与最终容器中的 Bean 一致。
易错点:构造器循环依赖抛 BeanCurrentlyInCreationException,可用 @Lazy 延迟注入绕过;但循环依赖本身多是设计坏味道,应优先考虑拆分职责。