当前位置: 首页 > news >正文

Spring循环依赖解决方案

Spring循环依赖解决方案引言循环依赖是Spring应用中常见的问题当两个或多个Bean相互依赖形成闭环时Spring容器无法正常创建这些Bean。Spring框架通过三级缓存机制解决了大部分单例Bean的循环依赖问题但某些场景下循环依赖仍然会导致启动失败。本文将深入分析Spring循环依赖的原理、三级缓存机制以及各种解决方案。一、循环依赖类型1.1 构造器循环依赖Service public class ServiceA { private final ServiceB serviceB; public ServiceA(ServiceB serviceB) { this.serviceB serviceB; } } Service public class ServiceB { private final ServiceA serviceA; public ServiceB(ServiceA serviceA) { this.serviceA serviceA; } } // ServiceA - ServiceB - ServiceA 循环依赖 // 构造器注入会导致启动失败1.2 Setter循环依赖Service public class ServiceA { private ServiceB serviceB; Autowired public void setServiceB(ServiceB serviceB) { this.serviceB serviceB; } } Service public class ServiceB { private ServiceA serviceA; Autowired public void setServiceA(ServiceA serviceA) { this.serviceA serviceA; } } // Setter注入可以通过三级缓存解决1.3 Prototype循环依赖Bean Scope(prototype) public ServiceA serviceA(ServiceB serviceB) { return new ServiceA(serviceB); } Bean Scope(prototype) public ServiceB serviceB(ServiceA serviceA) { return new ServiceB(serviceA); } // Prototype作用域Bean不支持循环依赖解决二、三级缓存机制2.1 缓存结构public class DefaultSingletonBeanRegistry { // 一级缓存已完成的单例Bean private final MapString, Object singletonObjects new ConcurrentHashMap(); // 二级缓存提前暴露的Bean未完成属性填充 private final MapString, Object earlySingletonObjects new ConcurrentHashMap(); // 三级缓存Bean创建工厂 private final MapString, ObjectFactory? singletonFactories new ConcurrentHashMap(); }2.2 获取Bean流程protected Object getSingleton(String beanName, boolean allowEarlyCreation) { // 1. 从一级缓存获取 Object singleton singletonObjects.get(beanName); // 2. 如果不存在且正在创建 if (singleton null isSingletonCurrentlyInCreation(beanName)) { synchronized (singletonObjects) { // 3. 从二级缓存获取 singleton earlySingletonObjects.get(beanName); if (singleton null) { // 4. 从三级缓存获取 ObjectFactory? factory singletonFactories.get(beanName); if (factory ! null) { singleton factory.getObject(); // 移动到二级缓存 earlySingletonObjects.put(beanName, singleton); singletonFactories.remove(beanName); } } } } return singleton; }三、循环依赖解决过程3.1 A依赖BB依赖A// 创建Bean A的流程 1. 调用 getSingleton(a) 2. 调用 createBean(a) 3. 添加到正在创建集合 4. 创建A实例添加到三级缓存 5. 属性填充发现依赖B 6. 调用 getSingleton(b) 7. 调用 createBean(b) 8. 创建B实例添加到三级缓存 9. 属性填充发现依赖A 10. 调用 getSingleton(a) 11. 从三级缓存获取A的ObjectFactory 12. 获取早期A移到二级缓存 13. 完成B的属性填充和初始化 14. B添加到一级缓存 15. 完成A的属性填充和初始化 16. A添加到一级缓存3.2 时序图A创建 - 三级缓存添加A工厂 - 填充B - B创建 - 三级缓存添加B工厂 - 填充A - 获取A工厂 - 创建早期A - 填充早期A给B - 完成B - 完成A四、构造器循环依赖4.1 无法解决的原因// 构造器注入必须在创建实例时完成 public ServiceA(ServiceB serviceB) { this.serviceB serviceB; // 这里需要B已创建 } public ServiceB(ServiceA serviceA) { this.serviceA serviceA; // 这里需要A已创建 } // 无法打破循环因为实例化就需要完整依赖4.2 解决方案Setter注入Service public class ServiceA { private ServiceB serviceB; Autowired public void setServiceB(ServiceB serviceB) { this.serviceB serviceB; } } Service public class ServiceB { private ServiceA serviceA; Autowired public void setServiceA(ServiceA serviceA) { this.serviceA serviceA; } }五、Lazy注解解决5.1 基本用法Service public class ServiceA { private final ServiceB serviceB; public ServiceA(Lazy ServiceB serviceB) { this.serviceB serviceB; } } Service public class ServiceB { private final ServiceA serviceA; public ServiceB(Lazy ServiceA serviceA) { this.serviceA serviceA; } }5.2 原理Lazy创建代理对象注入实际Bean在首次使用时才创建避免了初始化时的循环。// Lazy生成的是代理对象 public class ServiceA { private ServiceB serviceB; public ServiceA(LazyObjectFactoryServiceB lazyFactory) { this.serviceB lazyFactory.getObject(); } }六、DependsOn注解6.1 强制初始化顺序Service DependsOn(serviceA) public class ServiceB { private ServiceA serviceA; public ServiceB(ServiceA serviceA) { this.serviceA serviceA; } } Service public class ServiceA { // A会先于B创建 }6.2 使用场景Configuration DependsOn({dataSourceInitializer, cacheInitializer}) public class DatabaseConfig { // 确保初始化器先执行 }七、ObjectProvider延迟注入7.1 基本用法Service public class ServiceA { private final ObjectProviderServiceB serviceBProvider; public ServiceA(ObjectProviderServiceB serviceBProvider) { this.serviceBProvider serviceBProvider; } public void doSomething() { ServiceB serviceB serviceBProvider.getIfAvailable(); // 必要时获取 } }7.2 ObjectFactoryService public class ServiceB { private final ObjectFactoryServiceA serviceAFactory; public ServiceB(ObjectFactoryServiceA serviceAFactory) { this.serviceAFactory serviceAFactory; } public void doSomething() { ServiceA serviceA serviceAFactory.getObject(); } }八、重构解决循环8.1 提取公共依赖// Before: A - B - C - A 循环 // After: 提取C到新服务 Service public class CommonService { // 提取公共逻辑 } Service public class ServiceA { private CommonService commonService; } Service public class ServiceB { private CommonService commonService; }8.2 事件驱动解耦Service public class ServiceA { private final ApplicationEventPublisher publisher; public ServiceA(ApplicationEventPublisher publisher) { this.publisher publisher; } public void doSomething() { publisher.publishEvent(new OrderCreatedEvent(this)); } } Service public class ServiceB { EventListener public void handleOrderCreated(OrderCreatedEvent event) { // 异步处理不直接依赖 } }九、自定义Scope解决9.1 ThreadLocal ScopeComponent public class ThreadScope implements Scope { private final ThreadLocalMapString, Object threadScope ThreadLocal.withInitial(HashMap::new); Override public Object get(String name, ObjectFactory? objectFactory) { MapString, Object scope threadScope.get(); Object object scope.get(name); if (object null) { object objectFactory.getObject(); scope.put(name, object); } return object; } Override public Object remove(String name) { return threadScope.get().remove(name); } }十、Spring循环依赖配置10.1 允许早期代理spring: main: allow-circular-references: true # Spring Boot 2.6默认禁止10.2 代理模式Configuration public class ProxyConfig { Bean Scope(proxyMode ScopedProxyMode.TARGET_CLASS) public ServiceA serviceA() { return new ServiceA(); } }总结Spring的三级缓存机制解决了大部分单例Bean的Setter循环依赖问题但构造器循环依赖无法解决。通过Lazy注解、ObjectProvider、事件驱动等手段可以有效打破循环依赖。最根本的解决方案是通过合理的架构设计避免循环依赖的产生。
http://www.zskr.cn/news/1310423.html

相关文章:

  • ApkShellext2:3步让Windows文件管理器智能显示APK原生图标
  • WeChatExporter:基于iOS备份解析的微信聊天记录数据提取架构
  • CSS 伪类完全指南
  • 字符流中第一个只出现一次的字符-C++
  • 3个认知升级:重新定义魔兽世界宏编程的操作范式
  • 构建Telegram与私有AI模型桥接器:从原理到工程实践
  • STM32 串口通信:串口的接收和发送详解
  • d2s-editor:暗黑破坏神2存档编辑器的现代化Web解决方案
  • 如何让Windows资源管理器完美预览iPhone照片:HEIC缩略图插件全解析
  • 如何使用witr快速定位占用端口的神秘进程?完整指南
  • Oto 核心架构深度解析:Context 与 Player 的设计哲学
  • 内容创作团队如何利用多模型API提升图文生成效率
  • 告别单调终端:250+ Xshell配色方案让你的命令行焕然一新
  • 半导体IPO热潮:低营收高专利企业如何重塑资本估值与技术壁垒
  • 从手动点击到Python驱动:探索PyFluent如何重新定义CFD工作流自动化
  • FanControl终极指南:告别BIOS限制,打造个性化风扇控制方案
  • jQuery TreeTable:在表格中优雅展示树形结构的完整指南
  • VCS仿真总失败?手把手教你用TMAX的CPV功能快速定位ATPG Pattern问题
  • Hotkey Detective:3分钟定位Windows热键冲突的神器
  • 从零搭建到日常调试:一份给新手的 Kafka 命令行操作全流程指南
  • Freeplane思维导图终极指南:100+专业模板让你的思考效率翻倍
  • 智能报警器语音芯片选型与硬件设计实战指南
  • Windows变身AirPlay 2接收器:打破苹果生态壁垒的终极解决方案
  • Cursor AI编程助手扩展包:定制化规则提升代码生成质量与效率
  • 终极AMD Ryzen硬件调试指南:5分钟掌握SMU Debug Tool实战技巧
  • 实测,这个小程序真的可以免费压缩图片?10MB 一秒压到 1.6MB
  • MDX-M3-Viewer深度解析:浏览器端游戏模型渲染的全新范式
  • 2026年GEO优化合规测评:策略效果指标排名出炉 - 羊城派
  • 解决方案:如何3步自动化生成黑苹果EFI配置
  • 【ChatGPT SWOT分析黄金模板】:20年AI战略顾问亲授——5步生成高信效度SWOT报告(附可落地Prompt库)