【一文看懂Spring循环依赖】Spring循环依赖:从陷阱破局到架构涅槃

🌪️ Spring Boot循环依赖:从陷阱破局到架构涅槃

循环依赖如同莫比乌斯环上的蚂蚁,看似前进却永远困在闭环中。本文将带你拆解Spring中这一经典难题,从临时救火到根治重构,构建无懈可击的依赖体系。


🔥 一、致命闭环:当依赖链开始打结

📍 1. 循环依赖的三种形态
  • 构造器死锁(无解型)

    @Service
    public class OrderService {private final UserService userService;public OrderService(UserService userService) { // 启动即崩溃this.userService = userService; }
    }@Service
    public class UserService {private final OrderService orderService;public UserService(OrderService orderService) { // 相互等待初始化this.orderService = orderService; }
    }
    

    致命点:Spring无法通过三级缓存提前暴露Bean,直接抛出BeanCurrentlyInCreationException

  • 字段/Setter依赖(可救型)
    通过三级缓存机制可解:

    实例化OrderService
    暴露早期引用到三级缓存
    注入UserService时实例化UserService
    UserService从缓存拿到OrderService早期引用
    UserService初始化完成
    OrderService注入完整UserService
  • 异步方法闭环(隐蔽型)
    @Async@Transactional生成的代理对象会干扰依赖注入流程,导致看似无循环的代码在运行时崩溃


🛠️ 二、破局五式:从应急到根治

🧪 1. 急救方案:@Lazy的妙用与陷阱
@Service
public class OrderService {@Lazy  // 延迟注入关键注解@Autowired  private PaymentService paymentService; 
}

原理:生成代理对象占位,首次调用时初始化真实Bean,打破初始化死锁
风险警示

  • 过度使用导致代理对象泛滥,调用链复杂度指数级增长
  • 与构造器注入结合可能引发NPE(代理对象未被触发初始化)
⚙️ 2. 依赖注入改造:Setter vs 构造器

Setter注入救场

@Service
public class OrderService {private PaymentService paymentService;@Autowired  // 避免构造器死锁public void setPaymentService(PaymentService ps) {this.paymentService = ps;}
}

本质差异:构造器注入要求依赖项在实例化时完备,Setter注入允许先创建空壳再填充

🧱 3. 架构重构:三大根治术

① 抽离公共层(依赖倒置)

public interface PaymentProcessor { // 抽象接口void processPayment();
}@Service 
public class AlipayProcessor implements PaymentProcessor {...} // 实现1@Service 
public class WechatProcessor implements PaymentProcessor {...} // 实现2@Service
public class OrderService {private final PaymentProcessor processor; // 依赖抽象public OrderService(PaymentProcessor processor) {...} 
}

优势:符合SOLID原则,彻底切断双向依赖

② 事件驱动(终极解耦)

@Service
public class OrderService {@Autowiredprivate ApplicationEventPublisher eventPublisher;public void createOrder() {eventPublisher.publishEvent(new OrderCreatedEvent(this)); // 发布事件}
}@Service
public class InventoryService {@EventListener // 监听解耦public void reduceStock(OrderCreatedEvent event) {...} 
}

效果:订单服务与库存服务零直接依赖,通过事件总线通信

③ 门面模式(统一入口)

@Service
public class OrderFacade { // 门面服务@Autowired private OrderService orderService;@Autowired private PaymentService paymentService;public void executeOrder() {orderService.create();paymentService.charge();}
}

适用场景:多服务强关联但需避免循环调用


⚠️ 三、避坑指南:那些雪上加霜的操作

  1. 配置允许循环依赖(饮鸩止渴)

    spring.main.allow-circular-references=true # Spring Boot 2.6+前有效
    

    后果:高版本默认禁用此配置,且掩盖设计缺陷

  2. 原型Bean(Prototype)的循环依赖
    每次请求创建新实例,三级缓存失效,必须用@Lazy或重构

  3. @PostConstruct中的隐式循环

    @Service
    public class ServiceA {@Autowired ServiceB serviceB;@PostConstructpublic void init() { serviceB.register(this); // 触发ServiceB反向调用}
    }
    

    解决方案:改用ApplicationListener或事件驱动


🏆 四、最佳实践:让循环依赖无处遁形

  1. 防御性编码

    • 强制构造器注入:启动时暴露问题 > 运行时崩溃
    • 分层架构守护
      // ArchUnit检测循环依赖
      @ArchTest
      static final ArchRule no_cycles = slices().matching("com.yourapp.(*)..").should().beFreeOfCycles();
      
  2. 依赖可视化监控

    # 生成Bean依赖图
    curl http://localhost:8080/actuator/beans | jq '.contexts.context.beans'
    

    结合Spring Boot Actuator实时分析依赖链路


💎 结语:依赖的本质是契约

循环依赖的终极解决方案不在技术层面,而在架构认知。当我们用“服务能力”替代“服务调用”的视角设计系统时,模块间将自然形成单向流动的依赖河床。正如领域驱动设计中限界上下文(Bounded Context)的隔离艺术——每个上下文都是自治的王国,通过防腐层进行优雅的外交对话。

思考题:在微服务架构中,循环依赖会以怎样的形式存在?欢迎分享你的实战案例。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/web/83270.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

el-table封装自动滚动表格(适用大屏)

表格功能&#xff1a;自动滚动&#xff0c;鼠标移入停止滚动&#xff0c;移出继续滚动。如果想加触底加载新数据可以判断 scrollWrap.scrollTop和maxScrollTop大小来加载数据&#xff0c;另写逻辑。 <template><el-table ref"eltable" :data"tableDa…

Eureka REST 相关接口

可供非 Java 应用程序使用的 Eureka REST 操作。 appID 是应用程序的名称&#xff0c;instanceID 是与实例关联的唯一标识符。在 AWS 云中&#xff0c;instanceID 是实例的实例 ID&#xff1b;在其他数据中心&#xff0c;它是实例的主机名。 对于 XML/JSON&#xff0c;HTTP 的…

DSP——时钟树讲解

配置任何外设的第一步都要看一下时钟树,下图是DSP28377的时钟树: 由图所示DSP28377由4个时钟源,分别是INTOSC1、INTOSC2、XTAL、AUXCL INTOSC1:0M内部系统时钟,备用时钟,检测到系统时钟缺失自动连接到备用时钟,也作为看门狗时钟使用; INTOSC2:10M内部系统时钟,复位…

少量数据达到更好效果

九坤团队新作&#xff01;一条数据训练AI超越上万条数据 一 仅需一条无标签数据和10步优化 九坤团队训练了13,440个大模型&#xff0c;发现熵最小化 (EM) 仅需一条无标签数据和10步优化&#xff0c;就能实现与强化学习中使用成千上万条数据和精心设计的奖励机制所取得的性能提…

html - <mark>标签

<mark> 标签在HTML中用于高亮显示文本&#xff0c;通常用于突出显示某些重要的部分。它的默认样式通常是背景色为黄色&#xff0c;但你可以通过CSS自定义其外观。 1. 基本用法 <mark> 标签用于标记文本的高亮显示。它常用于搜索结果中&#xff0c;突出显示匹配的…

YOLOv8+ByteTrack:高精度人车过线统计系统搭建指南

文章目录 1. 引言2. YOLOv8简介3. 过线统计原理4. 代码实现4.1 环境准备4.2 基础检测代码4.3 过线统计实现4.4 完整代码示例5. 性能优化与改进5.1 多线程处理5.2 区域检测优化5.3 使用ByteTrack改进跟踪6. 实际应用中的挑战与解决方案7. 总结与展望1. 引言 目标检测是计算机视…

20、React常用API和Hook索引

这一小节中只给出一些API和Hook的索引&#xff0c;需要用到的时候可以去官网查询&#xff0c;如无必要此处不列出详细用法。React v1.19.1。 对Components的支持 以下是开发时通用的一些功能组件 APIdescription<Fragment>通常使用 <>…</> 代替&#xff0…

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…

HTML实现的2048游戏

以下是一个纯HTML实现的2048游戏代码&#xff0c;包含CSS和JavaScript&#xff1a; <!DOCTYPE html> <html> <head><meta charset"utf-8"><title>2048 Game</title><style>body {font-family: Arial, sans-serif;text-a…

使用Python 构建支持主流大模型与 Ollama 的统一接口平台

🧩 背景概述 近年来,随着大语言模型(LLM)的蓬勃发展,OpenAI 的 GPT 系列、Google 的 Gemini、Anthropic 的 Claude、以及开源的 Ollama 本地模型等,逐渐成为自然语言处理、智能问答、AI 助手等应用的基础组件。 开发者在使用这些模型时常面临如下问题: 各模型接口不统…

计算机系统概述(4)

计算机系统层次结构&#xff1a;硬件层、系统层、应用层。 计算机的基本硬件系统由运算器、控制器、存储器、输入设备和输出设备5大部件组成。 运算器、控制器等部件被集成在一起统称为中央处理单元CPU。 存储器是计算机系统中的记忆设备&#xff0c;分为内部存储器和外部存…

Linux 下的COW机制(copy-on-write)

Linux通过MMU进行虚拟地址到物理地址的转换&#xff0c;当进程执行fork()后&#xff0c;会把页中的权限设置为RD-ONLY&#xff08;只读&#xff09;。 MMU&#xff08;内存管理单元&#xff09; MMU本质是一个集成在CPU核心的硬件电路模块&#xff0c;其核心任务是实现…

客户案例 | 短视频点播企业海外视频加速与成本优化:MediaPackage+Cloudfront 技术重构实践

01技术背景与业务挑战 某短视频点播企业深耕国内用户市场&#xff0c;但其后台应用系统部署于东南亚印尼 IDC 机房。 随着业务规模扩大&#xff0c;传统架构已较难满足当前企业发展的需求&#xff0c;企业面临着三重挑战&#xff1a; ① 业务&#xff1a;国内用户访问海外服…

开发Vue.js组件的二三事

Vue.js作为一款渐进式JavaScript框架&#xff0c;其组件化开发模式是其核心优势之一。在多年的Vue开发实践中&#xff0c;我积累了一些组件开发的经验和思考&#xff0c;在此与大家分享。 组件设计原则 单一职责原则 每个组件应该只关注一个特定的功能或UI部分。如果一个组件…

实现多路视频截图预览之后上传到后台系统

********************父组件********************** <div class"camera-box" v-loading"i.loading"> <div class"camera-box-inner" v-for"(x, y) in i.children" :key"y children x.featureCode" v-show"…

分布式锁-Redisson实现

目录 本地锁的局限性 Redisson解决分布式锁问题 在分布式环境下&#xff0c;分布式锁可以保证在多个节点上的并发操作时数据的一致性和互斥性。分布式锁有多种实现方案&#xff0c;最常用的两种方案是&#xff1a;zookeeper和redis&#xff0c;本文介绍redis实现分布式锁方案…

【办公类-48-04】202506每月电子屏台账汇总成docx-5(问卷星下载5月范围内容,自动获取excel文件名,并转移处理)

背景需求&#xff1a; 1-4月电子屏表格&#xff0c;都是用这个代码将EXCEL数据整理成分类成3个WORD表格。 【办公类-48-04】20250118每月电子屏台账汇总成docx-4&#xff08;提取EXCLE里面1月份的内容&#xff0c;自制月份文件夹&#xff09;-CSDN博客文章浏览阅读1.2k次&…

【websocket】安装与使用

websocket安装与使用 1. 介绍2. 安装3. websocketpp常用接口4. Websocketpp使用4.1 服务端4.2 客户端 1. 介绍 WebSocket 是从 HTML5 开始支持的一种网页端和服务端保持长连接的 消息推送机制。 传统的 web 程序都是属于 “一问一答” 的形式&#xff0c;即客户端给服务器发送…

微算法科技(NASDAQ:MLGO)基于信任的集成共识和灰狼优化(GWO)算法,搭建高信任水平的区块链网络

随着数字化转型的加速&#xff0c;区块链技术作为去中心化、透明且不可篡改的数据存储与交换平台&#xff0c;正逐步渗透到金融、供应链管理、物联网等多个领域&#xff0c;探索基于信任的集成共识机制&#xff0c;并结合先进的优化算法来提升区块链网络的信任水平&#xff0c;…

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…