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

Spring 中的循环依赖问题:解决方案与三级缓存机制

目录

  • Spring 中的循环依赖问题:解决方案与三级缓存机制
    • 什么是循环依赖?
      • 循环依赖的定义
      • 循环依赖的举例
    • Spring 中的循环依赖类型
      • 1. 构造器注入引发的循环依赖
      • 2. Setter 注入引发的循环依赖
      • 3. 字段注入(@Autowired)引发的循环依赖
    • Spring 如何处理循环依赖
      • Spring 的三级缓存机制
      • Spring 三级缓存的工作流程
      • 三级缓存图示
      • Spring 的三级缓存机制的配置与实现
      • 解决循环依赖的实战案例
        • 使用构造器注入时的循环依赖
        • 解决方案:使用 Setter 注入
    • 总结与最佳实践

Spring 中的循环依赖问题:解决方案与三级缓存机制

在现代企业级应用中,Spring 是最常用的 Java 框架之一,它通过 IoC(控制反转)容器 实现了依赖注入(DI)和解耦的机制。然而,在某些复杂的 Bean 依赖关系中,我们经常会遇到 循环依赖 问题。本文将深入解析 Spring 中的循环依赖问题,探讨其原理、常见的解决方案,并结合实际案例提供最佳实践。此外,我们还将介绍 Spring 如何通过 三级缓存机制 解决循环依赖的问题,帮助开发者更好地理解和应用这一机制。


什么是循环依赖?

循环依赖的定义

在 Spring 中,循环依赖 是指两个或多个 Bean 之间相互依赖,形成一个闭环。例如,Bean A 依赖于 Bean B,而 Bean B 又依赖于 Bean A。这样一来,Spring 容器就无法确定应该先实例化哪个 Bean,从而导致死锁,无法完成 Bean 的初始化。

循环依赖的举例

假设有两个 Bean AB,它们之间形成了相互依赖的关系:

@Component
public class A {private B b;@Autowiredpublic A(B b) {this.b = b;}
}@Component
public class B {private A a;@Autowiredpublic B(A a) {this.a = a;}
}

在这个例子中,A 依赖 B,而 B 又依赖 A,形成了一个循环依赖的闭环。


Spring 中的循环依赖类型

Spring 框架中的循环依赖主要有三种形式,分别是 构造器注入Setter 注入字段注入(@Autowired)

1. 构造器注入引发的循环依赖

构造器注入需要在实例化 Bean 时就注入其依赖,而 Spring 并不能在实例化过程中获取到还未完全初始化的 Bean。因此,如果两个 Bean 互相依赖,Spring 无法解决循环依赖问题。

@Component
public class A {private B b;@Autowiredpublic A(B b) {this.b = b;}
}@Component
public class B {private A a;@Autowiredpublic B(A a) {this.a = a;}
}

如上所示,A 依赖 BB 又依赖 A,Spring 在尝试实例化 A 时,需要先实例化 B,但是 B 又依赖于 A,这就形成了一个闭环。

2. Setter 注入引发的循环依赖

Setter 注入相对于构造器注入,在解决循环依赖时更为灵活。Spring 会先创建 AB 的实例,再通过 Setter 方法注入依赖关系,因此不会出现构造器注入时的循环依赖问题。

@Component
public class A {private B b;@Autowiredpublic void setB(B b) {this.b = b;}
}@Component
public class B {private A a;@Autowiredpublic void setA(A a) {this.a = a;}
}

在这种情况下,Spring 会先实例化 AB,然后通过 Setter 方法注入依赖,从而避免了循环依赖的问题。

3. 字段注入(@Autowired)引发的循环依赖

字段注入通过 @Autowired 注解直接注入依赖,Spring 通过反射机制自动为字段赋值。如果存在循环依赖,Spring 仍然能够处理,但前提是你使用的是 单例 Bean懒加载,否则会导致初始化失败。

@Component
public class A {@Autowiredprivate B b;
}@Component
public class B {@Autowiredprivate A a;
}

在这种情况下,Spring 会先实例化 AB 的代理对象,并将实际的 Bean 注入到其中,解决了循环依赖的问题。


Spring 如何处理循环依赖

Spring 的三级缓存机制

为了避免循环依赖,Spring 使用了 三级缓存机制 来处理单例 Bean 的创建。具体来说,Spring 会通过以下三种缓存来管理 Bean 的生命周期和依赖关系:

  1. 一级缓存singletonObjects):存储已经完全初始化的 Bean 对象。
  2. 二级缓存earlySingletonObjects):存储正在创建中的 Bean 的早期引用,帮助避免循环依赖。
  3. 三级缓存singletonFactories):存储 ObjectFactory,用于延迟生成 Bean 实例。

Spring 三级缓存的工作流程

  1. 从一级缓存获取 Bean

    • 如果该 Bean 已经创建并存在于一级缓存中,直接返回 Bean 实例。
  2. 创建 Bean 时

    • 如果 Bean 不在一级缓存中,Spring 开始实例化该 Bean。
    • 如果该 Bean 在创建过程中,Spring 会将其早期引用放入 二级缓存 中,避免循环依赖问题。
  3. 代理工厂的使用

    • 如果存在循环依赖,Spring 会将创建 Bean 的工厂放入 三级缓存 中,这样在依赖注入过程中返回代理对象。
  4. 依赖注入完成后

    • 完成依赖注入和初始化后,真实的 Bean 会替换掉代理对象,并放入 一级缓存

三级缓存图示

在这里插入图片描述

Spring 的三级缓存机制的配置与实现

Spring 的三级缓存机制默认启用,开发者不需要手动配置即可使用这一机制。它是 Spring 内部实现的一部分,位于 DefaultListableBeanFactory 类中。三级缓存通过 singletonObjectsearlySingletonObjectssingletonFactories 属性来管理 Bean 的生命周期。

不过,如果你需要对缓存进行优化或定制,可以通过自定义 BeanFactoryBeanPostProcessor 来实现。例如,使用 setAllowCircularReferences(true) 允许 Spring 处理循环依赖。

@Configuration
public class BeanFactoryConfig {@Beanpublic BeanFactoryCustomizer<DefaultListableBeanFactory> beanFactoryCustomizer() {return beanFactory -> {// 设置自定义的 BeanFactory 配置beanFactory.setAllowCircularReferences(true);};}
}

解决循环依赖的实战案例

使用构造器注入时的循环依赖
@Component
public class A {private B b;@Autowiredpublic A(B b) {this.b = b;}
}@Component
public class B {private A a;@Autowiredpublic B(A a) {this.a = a;}
}
解决方案:使用 Setter 注入
@Component
public class A {private B b;@Autowiredpublic void setB(B b) {this.b = b;}
}@Component
public class B {private A a;@Autowiredpublic void setA(A a) {this.a = a;}
}

通过使用 Setter 注入,Spring 可以顺利地实例化并注入依赖,从而解决了循环依赖的问题。


总结与最佳实践

Spring 中的循环依赖问题虽然看似复杂,但通过三级缓存机制,Spring 能够高效地处理单例 Bean 的创建和依赖注入。为了解决循环依赖问题,推荐以下最佳实践:

  1. 避免使用构造器注入,特别是在处理相互依赖的 Bean 时。
  2. 使用 Setter 注入或字段注入,这些方式能够有效避免循环依赖。
  3. 合理设计 Bean 之间的依赖关系,尽量减少紧耦合,提高系统的解耦度。
  4. 使用 @Lazy 注解 延迟加载 Bean,避免过早的依赖注入。

通过理解和应用这些技术,可以有效解决 Spring 中的循环依赖问题,提升应用的性能和可维护性。

相关文章:

Spring 中的循环依赖问题:解决方案与三级缓存机制

目录 Spring 中的循环依赖问题&#xff1a;解决方案与三级缓存机制什么是循环依赖&#xff1f;循环依赖的定义循环依赖的举例 Spring 中的循环依赖类型1. 构造器注入引发的循环依赖2. Setter 注入引发的循环依赖3. 字段注入&#xff08;Autowired&#xff09;引发的循环依赖 Sp…...

ios接入穿山甲【Swift】

1.可接入的广告&#xff0c;点击右下角查看接入文档 https://www.csjplatform.com/union/media/union/download/groMore 2.进入接入文档&#xff0c;选择最新版本进行接入 pod Ads-CN-Beta,6.8.0.2pod GMGdtAdapter-Beta, 4.15.22.0pod GDTMobSDK,4.15.30pod KSAdSDK,3.3.74.0p…...

蓝桥杯大模板

init.c void System_Init() {P0 0x00; //关闭蜂鸣器和继电器P2 P2 & 0x1f | 0xa0;P2 & 0x1f;P0 0x00; //关闭LEDP2 P2 & 0x1f | 0x80;P2 & 0x1f; } led.c #include <LED.H>idata unsigned char temp_1 0x00; idata unsigned char temp_old…...

电脑一直不关机会怎么样?电脑长时间不关机的影响

现代生活中&#xff0c;许多人会让自己的电脑24小时不间断运行&#xff0c;无论是为了持续的工作、娱乐&#xff0c;还是出于忘记关机的习惯。然而&#xff0c;电脑长时间不关机&#xff0c;除了提供便利之外&#xff0c;也可能对设备的健康产生一系列影响。本文将为大家介绍电…...

vue3 当页面显示了 p/span/div 标签 想要转换成正常文字

返回值有标签出现时&#xff0c;使用v-html 解决 <p>{{ item.content }}</p> //页面直接显示接口返回的带标签的数据 <p v-html"item.content "></p> //转换成html文件 显示正常文字各种样式 问题&#xff1a; 解决&#xff1a;v-html 显…...

Elasticsearch 8.18 中提供了原生连接 (Native Joins)

作者&#xff1a;来自 Elastic Costin Leau 探索 LOOKUP JOIN&#xff0c;这是一条在 Elasticsearch 8.18 的技术预览中提供的新 ES|QL 命令。 很高兴宣布 LOOKUP JOIN —— 这是一条在 Elasticsearch 8.18 的技术预览中提供的新 ES|QL 命令&#xff0c;旨在执行左 joins 以进行…...

java CountDownLatch用法简介

CountDownLatch倒计数锁存器 CountDownLatch&#xff1a;用于协同控制一个或多个线程等待在其他线程中执行的一组操作完成&#xff0c;然后再继续执行 CountDownLatch用法 构造方法&#xff1a;CountDownLatch(int count)&#xff0c;count指定等待的条件数&#xff08;任务…...

k8s蓝绿发布

k8s蓝绿发布 什么是蓝绿部署K8S中如何实现蓝绿部署k8s蓝绿部署流程图 什么是蓝绿部署 参考: https://youtu.be/CLq_hA0lAd0 https://help.coding.net/docs/cd/best-practice/blue-green.html 蓝绿部署最早是由马丁福勒 2010年在他的博客中提出. 蓝绿部署是一种软件部署策略,用…...

链接世界:计算机网络的核心与前沿

计算机网络引言 在数字化时代&#xff0c;计算机网络已经成为我们日常生活和工作中不可或缺的基础设施。从简单的局域网&#xff08;LAN&#xff09;到全球互联网&#xff0c;计算机网络将数以亿计的设备连接在一起&#xff0c;推动了信息交换、资源共享以及全球化的进程。 什…...

记录Docker部署CosyVoice V2.0声音克隆

#记录工作 CosyVoice 是由 FunAudioLLM 团队开发的一个开源多语言大规模语音生成模型&#xff0c;提供了从推理、训练到部署的全栈解决方案。 项目地址&#xff1a; https://github.com/FunAudioLLM/CosyVoice.git 该项目目前从v1.0版本迭代到v2.0版本&#xff0c;但是在Wind…...

MCU刷写——HEX与S19文件互转详解及Python实现

工作之余来写写关于MCU的Bootloader刷写的相关知识,以免忘记。今天就来聊聊Hex与S19这这两种文件互相转化,我是分享人M哥,目前从事车载控制器的软件开发及测试工作。 学习过程中如有任何疑问,可底下评论! 如果觉得文章内容在工作学习中有帮助到你,麻烦点赞收藏评论+关注走…...

全链路开源数据平台技术选型指南:六大实战工具链解析

在数字化转型加速的背景下&#xff0c;开源技术正重塑数据平台的技术格局。本文深度解析数据平台的全链路架构&#xff0c;精选六款兼具创新性与实用性的开源工具&#xff0c;涵盖数据编排、治理、实时计算、联邦查询等核心场景&#xff0c;为企业构建云原生数据架构提供可落地…...

C++学习:六个月从基础到就业——面向对象编程:封装、继承与多态

C学习&#xff1a;六个月从基础到就业——面向对象编程&#xff1a;封装、继承与多态 本文是我C学习之旅系列的第九篇技术文章&#xff0c;主要讨论C中面向对象编程的三大核心特性&#xff1a;封装、继承与多态。这些概念是理解和应用面向对象设计的关键。查看完整系列目录了解…...

Golang Event Bus 最佳实践:使用 NSQite 实现松耦合架构

Go Event Bus 最佳实践&#xff1a;使用 NSQite 实现松耦合架构 什么是 Event Bus&#xff1f; Event Bus&#xff08;事件总线&#xff09;是一种消息传递模式&#xff0c;它允许应用程序的不同组件通过发布/订阅机制进行通信&#xff0c;而不需要直接相互依赖。这种模式特别…...

独家!美团2025校招大数据题库

推荐阅读文章列表 2025最新大数据开发面试笔记V6.0——试读 我的大数据学习之路 面试聊数仓第一季 题库目录 Java 1.写一个多线程代码 2.写一个单例代码 3.LinkedBlockingQueue原理 4.模板设计模式 5.如何设计一个 生产者-消费者队列 6.堆内存和栈内存 7.ThreadLo…...

用 C++ 模拟客户端渲染中的分步数据加载

用 C++ 模拟客户端渲染中的分步数据加载 引言 在前端开发中,客户端渲染是一种常见的技术,它允许页面在加载后动态地更新内容。通常,页面会先展示一个基本的骨架,然后再逐步加载和渲染具体的数据。本文将介绍如何使用 C++ 编写一个简单的程序来模拟客户端渲染中的这种分步…...

Dify智能体平台源码二次开发笔记(5) - 多租户的SAAS版实现(2)

目录 前言 用户的查询 controller层 添加路由 service层 用户的添加 controller层 添加路由 service层-添加用户 service层-添加用户和租户关系 验证结果 结果 前言 完成租户添加功能后&#xff0c;下一步需要实现租户下的用户管理。基础功能包括&#xff1a;查询租…...

Linux的目录结构(介绍,具体目录结构)

目录 介绍 具体目录结构 简洁的目录解释 详细的目录解释 介绍 Linux的文件系统是采用级层式的树状目录结构&#xff0c;在此结构的最上层是根目录“/”。Linux的世界中&#xff0c;一切皆文件&#xff08;比如&#xff1a;Linux会把硬件映射成文件来管理&#xff09; 具体目…...

如何用 esProc 补充数据库 SQL 的缺失能力

某些数据库 SQL 缺失必要的能力&#xff0c;通常要编写大段的代码&#xff0c;才能间接实现类似的功能&#xff0c;有些情况甚至要改用存储过程&#xff0c;连结构都变了。常见的比如&#xff1a;生成时间序列、保持分组子集、动态行列转换、自然序号、相对位置、按序列和集合生…...

晶晨线刷工具下载及易错点说明:Key文件配置错误/mac剩余数为0解决方法

晶晨线刷工具下载及易错点说明&#xff1a;Key文件配置错误&#xff0f;mac剩余数为0解决方法 各种版本晶晨线刷工具下载&#xff1a; 晶晨线刷工具易出错点故障解决方法&#xff1a; 1、晶晨线刷工具加载固件的时候提示mac红字且剩余数为0的解决办法 很多同学可能会与遇到加…...

论文阅读:Invertible Grayscale

这是一篇 ACM Transactions on Graphic 上的文章&#xff0c;这篇文章中介绍的应用还挺有意思的&#xff0c;关于可逆的图像灰度化。 Abstract 一旦彩色图像被转换为灰度图像&#xff0c;人们普遍认为&#xff0c;即使采用最先进的彩色化方法&#xff0c;原始颜色也无法完全恢…...

linux下使用php修改php.ini的session.save_path无效的解决办法

linux下安装php的组合还是php-fpm和nginx&#xff0c;其实已经安装好了&#xff0c;网站已经能够跑起来了&#xff0c;但是遇到后台登录的时候验证码一直不对&#xff0c;看了下报错&#xff0c;session无法存储&#xff0c;于是新增了一个phpinfo文件&#xff0c;使用web查看下…...

关于ResNet和FPN的一份介绍

在这篇文章中我将介绍ResNet和FPN这两个深度学习中重要的技术。 一、ResNet-50/101 首先我们先来看ResNet技术&#xff1a; 1.1 概述 ResNet技术是基于残差学习&#xff0c;引入Bottleneck技术以及Shortcut Connection技术&#xff0c;而去解决神经网络中的退化问题。 1.2…...

以技术的形式实现发票真伪的快速查验-Android发票查验接口

对于企业而言&#xff0c;假票入账不仅可能导致企业财务损失&#xff0c;更会引发一系列法律风险&#xff0c;因此精准、高效的发票查验服务成为了企业运营不可或缺的支持。发票验真服务接口&#xff0c;正是一款能满足这些需求&#xff0c;助力企业摆脱繁琐流程、提升工作效率…...

Python实现贪吃蛇三

上篇文章Python实现贪吃蛇一&#xff0c;实现了一个贪吃蛇的基础版本。后面第二篇文章Python实现贪吃蛇二修改了一些不足&#xff0c;但最近发现还有两点需要优化&#xff1a; 1、生成食物的时候有概率和记分牌重合 2、游戏缺少暂停功能 先看生成食物的时候有概率和记分牌重合的…...

Docker 中多个容器之间的通信

在 Docker 中&#xff0c;多个容器之间的通信可以通过以下几种主要方式实现&#xff0c;具体选择取决于网络需求、隔离性及管理复杂度&#xff1a; 一、自定义 Bridge 网络&#xff08;推荐&#xff09; 通过创建自定义的 Docker 网络&#xff0c;容器可以加入同一网络并通过容…...

AI大模型学习九:‌Sealos cloud+k8s云操作系统私有化一键安装脚本部署完美教程

一、说明 ‌Sealos‌是一款基于Kubernetes&#xff08;K8s&#xff09;的云操作系统发行版&#xff0c;它将K8s以及常见的分布式应用如Docker、Dashboard、Ingress等进行了集成和封装&#xff0c;使得用户可以在不深入了解复杂的K8s底层原理的情况下&#xff0c;快速搭建起一个…...

详解关于VS配置好Qt环境之后但无法打开ui界面

目录 找到Qt安装目录中designer.exe的路径 找到vs中的解决方案资源管理器 右键ui文件&#xff0c;找到打开方式 点击添加 然后把前面designer.exe的路径填到程序栏中&#xff0c;点击确定 然后设置为默认值&#xff0c;并点击确定 当在vs中配置好Qt环境之后&#xff0c;但…...

Python Cookbook-6.5 继承的替代方案——自动托管

任务 你需要从某个类或者类型继承&#xff0c;但是需要对继承做一些调整。比如&#xff0c;需要选择性地隐藏某些基类的方法&#xff0c;而继承并不能做到这一点。 解决方案 继承是很方便的&#xff0c;但它并不是万用良药。比如&#xff0c;它无法让你隐藏基类的方法或者属…...

【深度学习与大模型基础】第9章-条件概率以及条件概率的链式法则

简单理解条件概率 条件概率就是在已知某件事发生的情况下&#xff0c;另一件事发生的概率。用数学符号表示就是&#xff1a; P(A|B) 在B发生的前提下&#xff0c;A发生的概率。 计算机例子&#xff1a;垃圾邮件过滤 假设你写了一个程序来自动判断邮件是否是垃圾邮件&#xf…...