spring通过三级缓存中objectfactory提前暴露半成品bean,使a、b在未完全初始化时可相互获取早期引用,从而打破递归死锁;构造器注入因无法暴露半成品而失败。

循环依赖在 Spring 中本质是“谁先创建”的问题,而工厂注入(即通过 ObjectFactory 或三级缓存中的 ObjectFactory)正是 Spring 破解这一死锁的关键机制。它不靠“强行顺序”,而是靠“提前暴露半成品 + 延迟真正初始化”来实现递归终止——不是消除依赖环,而是让环中每个节点都能拿到一个可用的代理或早期引用。
工厂注入如何打破递归调用链
Spring 的三级缓存中,singletonFactories(三级缓存)存储的是 ObjectFactory,它是一个延迟执行的工厂函数。当 A 依赖 B、B 又依赖 A 时:
- A 实例化后,立即注册其
ObjectFactory到三级缓存,但不填充属性; - 轮到注入 B 时,发现 B 依赖 A,此时不等待 A 完成初始化,而是调用 A 的
ObjectFactory获取早期对象(可能带 AOP 代理); - 这个早期对象被放入二级缓存,并注入 B;
- B 初始化完成后放入一级缓存,再回过头完成 A 的属性注入;
- 整个过程没有无限递归,因为每次“获取依赖”都走缓存查表 + 工厂触发,而非重新进入 createBean 流程。
为什么构造器注入会失败
构造器注入要求 Bean 实例化时就必须提供所有依赖参数,而此时对方还未实例化——ObjectFactory 还没机会被调用,三级缓存也尚未建立对应条目。换句话说:
- 构造器阶段无法“提前暴露”,必须等依赖完全就绪;
- 没有中间态可借,递归直接卡在 new 实例这一步;
- 所以 Spring 在构造器注入场景下,不启用三级缓存机制,也就无法终止递归。
手动使用 ObjectProvider 实现可控工厂注入
比起 @Lazy 的隐式代理,ObjectProvider 是显式、安全的工厂注入方式,适合需要按需解析且避免空指针的场景:
- 它包装了底层
ObjectFactory,支持getIfAvailable()、stream()等安全操作; - 不会提前触发 Bean 创建,只在真正调用
getObject()时才走容器查找流程; - 配合作用域(如 prototype)也能工作,比
@Lazy更灵活。
示例:
@Service<br>public class OrderService {<br> private final ObjectProvider<paymentservice> paymentServiceProvider;<br><br> public OrderService(ObjectProvider<paymentservice> paymentServiceProvider) {<br> this.paymentServiceProvider = paymentServiceProvider;<br> }<br><br> public void process() {<br> PaymentService payment = paymentServiceProvider.getIfAvailable();<br> if (payment != null) {<br> payment.execute();<br> }<br> }<br>}</paymentservice></paymentservice>
递归终止的边界条件是什么
Spring 能成功终止递归,依赖三个硬性前提:
- Bean 必须是单例(只有单例才进三级缓存);
- 依赖注入方式必须是非构造器的(字段或 setter,允许分阶段完成);
- 不能存在多层嵌套的原型 Bean 或自定义 Scope,否则缓存失效,工厂无意义。
一旦这三个条件满足,Spring 就能靠工厂注入把“递归创建”转化为“查缓存 + 触发工厂”,自然终结调用栈增长。











