欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 维修 > Spring 是如何解决循环依赖问题的?

Spring 是如何解决循环依赖问题的?

2025/6/30 13:39:31 来源:https://blog.csdn.net/SOS5418818845/article/details/145691281  浏览:    关键词:Spring 是如何解决循环依赖问题的?

Spring如何巧妙化解循环依赖?三级缓存机制深度解析

在复杂的系统架构中,Bean之间的循环依赖问题如同"先有鸡还是先有蛋"的哲学难题。Spring框架通过独特的三级缓存机制,成功破解了这个困局。本文将深入剖析Spring解决循环依赖的核心原理,揭示其精妙的设计思想。


一、循环依赖的三种形态
  1. 构造器循环依赖

    @Service
    public class ServiceA {private ServiceB serviceB;public ServiceA(ServiceB serviceB) { /*...*/ }
    }@Service
    public class ServiceB {private ServiceA serviceA;public ServiceB(ServiceA serviceA) { /*...*/ }
    }

    ❌ 无法解决:Bean未实例化前无法暴露引用

  2. Setter方法循环依赖

    @Service
    public class ServiceA {private ServiceB serviceB;@Autowiredpublic void setServiceB(ServiceB serviceB) { /*...*/ }
    }

    ✅ 可解决:Spring主攻方向

  3. 原型(Prototype)作用域循环依赖
    ❌ 无法解决:不缓存Bean实例


二、三级缓存机制揭秘

Spring通过三个Map容器形成防御体系:

缓存级别数据结构存储内容
singletonObjectsConcurrentHashMap完整初始化的单例Bean
earlySingletonObjectsHashMap提前暴露的原始Bean(未填充属性)
singletonFactoriesHashMap对象工厂(用于生成早期引用)
工作流程(以ServiceA和ServiceB为例):
  1. 开始创建ServiceA

  2. 实例化ServiceA(此时对象尚未初始化)

  3. 将ServiceA的ObjectFactory存入singletonFactories

  4. 填充ServiceA属性时发现依赖ServiceB

  5. 开始创建ServiceB

  6. 实例化ServiceB后将其ObjectFactory存入缓存

  7. 填充ServiceB属性时通过三级缓存获取ServiceA的早期引用

  8. ServiceB完成初始化,存入singletonObjects

  9. ServiceA获得ServiceB实例,完成属性填充

  10. ServiceA完成初始化,升级为完整Bean


三、源码层面的实现

DefaultSingletonBeanRegistry中:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}return singletonObject;
}

四、开发最佳实践
  1. 优先使用Setter/Field注入

  2. 避免构造器循环依赖

    // 错误示例:启动时将抛出BeanCurrentlyInCreationException
    @Service
    public class CircularDependencyA {private CircularDependencyB circB;public CircularDependencyA(CircularDependencyB circB) { /*...*/ }
    }

  3. 谨慎使用@Lazy注解

    @Service
    public class ServiceA {private final ServiceB serviceB;public ServiceA(@Lazy ServiceB serviceB) { // 延迟初始化解决构造器依赖}
    }
  4. 定期检测循环依赖

    # 启动时添加检测参数
    spring.main.allow-circular-references=false

五、设计思想启示
  1. 空间换时间:通过缓存中间状态提升效率

  2. 提前暴露:不追求"完美对象",允许半成品流转

  3. 分层防御:三级缓存形成渐进式处理机制

  4. 工厂模式:ObjectFactory延迟实例化控制


结语

Spring的三级缓存设计展现了经典框架的智慧:通过缓存不同状态的Bean对象,在保持单例特性的同时,巧妙地打破了循环依赖的死锁。理解这一机制不仅能帮助开发者规避陷阱,更能启发我们设计复杂系统时的分层处理思想。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com