spring问题总结

循环依赖

循环依赖指的是两个或多个 Bean 相互依赖,形成一个闭环。这样的依赖关系会导致 Spring 容器无法正确创建和注入这些 Bean。

循环依赖会带来以下问题:

Bean 创建失败:Spring 容器在实例化一个 Bean 时,发现它依赖于另一个尚未创建的 Bean。如果两个 Bean 形成了循环依赖,Spring 就无法决定从哪个 Bean 开始创建,从而导致无法完成 Bean 的实例化和依赖注入。
内存泄漏风险:如果循环依赖没有得到解决,可能会导致一些 Bean 被创建但是无法被销毁,从而增加内存的占用,造成内存泄漏。

情景

假设有两个 Bean,A 和 B,它们之间有 构造器注入 关系,并且互相依赖:

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

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

循环依赖死锁的过程
A Bean 初始化开始:Spring 会尝试创建 A Bean 的实例,进入 A 的构造器(因为 A 依赖 B,所以 Spring 会尝试创建 B 的实例)。

B Bean 初始化开始:在创建 B 时,Spring 会进入 B 的构造器,发现 B 依赖 A,此时 A 尚未完全初始化,所以 Spring 会尝试创建 A 的实例,结果又回到了第 1 步。

这时,A 和 B 的实例化相互等待,没有任何一个 Bean 能够完成初始化。这种情况下,程序进入死锁状态。

解决

Spring 在默认情况通过 三级缓存 来解决单例 Bean 的循环依赖,但这只是解决一部分问题。

三级缓存怎么帮尽量解决循环依赖问题

Spring 的 三级缓存 是指 Spring 容器在创建 Bean 时,管理了三个不同的缓存阶段,主要解决的是 循环依赖 的问题。Spring 会通过这三级缓存,避免在循环依赖中死锁

具体流程:
一级缓存:Spring 会先尝试从一级缓存(通常是 singletonObjects)获取 Bean 实例。如果已经创建过,就直接返回该实例。

二级缓存:如果 Bean 还没有创建(即一级缓存中没有),Spring 会尝试从二级缓存(通常是 earlySingletonObjects)中获取该 Bean 实例。如果该 Bean 正在初始化中,并且有其他 Bean 依赖它,那么该 Bean 会被放入二级缓存(半成品)中。

三级缓存:如果该 Bean 还没有完成初始化,那么 Spring 会在三级缓存中保存该 Bean 的工厂(ObjectFactory)。当其他 Bean 依赖它时,可以通过工厂方法获得 Bean,从而避免了死锁问题。

@lazy 懒加载解决

当我们在 @Autowired 注解上使用 @Lazy 时,Spring 不会立即注入依赖的 Bean,而是会 推迟依赖的注入,直到该依赖真的被需要时才会注入。这样,如果 A 依赖 B,而 B 依赖 A,Spring 就不会在容器初始化时进入死锁状态,而是在需要时再去初始化其中一个 Bean。、

@Component
public class A {
    private final B b;
    
    @Autowired
    @Lazy
    public A(B b) {
        this.b = b;
    }
}

@Component
public class B {
    private final A a;
    
    @Autowired
    @Lazy
    public B(A a) {
        this.a = a;
    }
}

好的!我们可以深入探讨一下 循环依赖 会导致死锁的情况,特别是在 Spring 中如何通过懒加载解决这个问题。

  1. 什么是循环依赖?
    循环依赖指的是两个或多个 Bean 相互依赖的情况,形成一个闭环。例如:

Bean A 依赖 Bean B
Bean B 又依赖 Bean A
在 Spring 容器中,默认情况下,所有的 Bean 都是 单例 的,也就是说 Spring 会确保每个 Bean 只有一个实例。如果在 Bean 初始化的过程中,遇到循环依赖,Spring 就会按照默认的创建流程进行实例化,但如果没有特别的设计,可能会造成死锁或无限等待。

  1. 为什么循环依赖会造成死锁?
    在 Spring 中,循环依赖发生时,通常是因为依赖关系的 Bean 还没有初始化完成。Spring 会先为某个 Bean 创建一个实例并开始依赖注入,但如果这个 Bean 的构造方法中或其他属性中有对另一个 Bean 的依赖,而该依赖的 Bean 还没有初始化完成,就会出现问题。

死锁的情形
假设有两个 Bean,A 和 B,它们之间有 构造器注入 关系,并且互相依赖:

java
复制代码
@Component
public class A {
private final B b;

@Autowired
public A(B b) {
    this.b = b;
}

}

@Component
public class B {
private final A a;

@Autowired
public B(A a) {
    this.a = a;
}

}
在上面的代码中,A 和 B 相互依赖,且使用了 构造器注入。

  1. 循环依赖死锁的过程
    A Bean 初始化开始:Spring 会尝试创建 A Bean 的实例,进入 A 的构造器(因为 A 依赖 B,所以 Spring 会尝试创建 B 的实例)。

B Bean 初始化开始:在创建 B 时,Spring 会进入 B 的构造器,发现 B 依赖 A,此时 A 尚未完全初始化,所以 Spring 会尝试创建 A 的实例,结果又回到了第 1 步。

这时,A 和 B 的实例化相互等待,没有任何一个 Bean 能够完成初始化。这种情况下,程序进入死锁状态。

  1. 如何解决循环依赖死锁?
    Spring 通过 三级缓存机制 来解决 单例 Bean 的循环依赖,在这种机制下,Spring 容器会采用一种延迟初始化的方式,在 Bean 完成初始化之前 暂时返回一个代理对象,保证依赖关系能够被正常注入。

如何使用懒加载解决死锁问题?
懒加载(@Lazy)是 Spring 提供的一种 延迟加载 Bean 的机制。通过 @Lazy 注解,Spring 会推迟 Bean 的初始化,直到该 Bean 第一次被使用时才会真正创建。这样可以避免循环依赖的问题,因为懒加载的 Bean 在容器启动时不会被立即创建,直到它真的被需要的时候才会创建,从而打破循环依赖的死锁。

  1. 懒加载(@Lazy)如何解决循环依赖死锁?
    当我们在 @Autowired 注解上使用 @Lazy 时,Spring 不会立即注入依赖的 Bean,而是会 推迟依赖的注入,直到该依赖真的被需要时才会注入。这样,如果 A 依赖 B,而 B 依赖 A,Spring 就不会在容器初始化时进入死锁状态,而是在需要时再去初始化其中一个 Bean。

懒加载解决循环依赖的例子
使用 @Lazy 注解可以推迟 Bean 的初始化,避免循环依赖的死锁:

java
复制代码
@Component
public class A {
private final B b;

@Autowired
@Lazy
public A(B b) {
    this.b = b;
}

}

@Component
public class B {
private final A a;

@Autowired
@Lazy
public B(A a) {
    this.a = a;
}

}
执行流程:
A Bean 初始化开始:Spring 开始创建 A Bean 的实例。

A Bean 中的 B 依赖被延迟初始化:由于 A 中的 B 被标记为 @Lazy,Spring 不会立即初始化 B,而是把这个依赖注入的操作推迟,直到 B 被真正需要时才会去初始化 B。

B Bean 初始化开始:Spring 开始创建 B Bean 的实例。

B Bean 中的 A 依赖被延迟初始化:类似的,B 中的 A 也被标记为 @Lazy,因此 Spring 不会立即初始化 A,而是等到 A 被需要时再去初始化。

最终成功注入:由于使用了懒加载,Spring 避免了在容器启动时进行初始化的死锁,循环依赖得以打破,最终 A 和 B 可以正常完成依赖注入

使用setter方式注入

另一种解决方法是使用 setter 注入方式。通过 setter 注入,Spring 可以先创建 Bean 的实例,然后在稍后的阶段注入依赖。这种方式能够有效解决部分循环依赖问题,因为 Spring 容器先实例化 Bean,再通过 setter 方法注入依赖,而不是在构造器中就需要依赖完成。

@Component
public class A {
    private B b;

    @Autowired
    public void setB(B b) {
        this.b = b;
    }
}

@Component
public class B {
    private A a;

    @Autowired
    public void setA(A a) {
        this.a = a;
    }
}

总结

@lazy 和 setter 方式都能解决单例模式下的循环依赖,他们都有延迟的概念
setter 注入 是在 实例化后的某个阶段 执行注入操作(即注入依赖)
而懒加载则是 推迟整个 Bean 的初始化,直到它被实际需要时才实例化。

持续更新。。。。。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

WD5105同步降压转换器:9.2V-95V宽电压输入,4.5A大电流输出,95%高效率,多重保护功能

概述 • WD5105同步降压转换器 • 封装形式:QFN-20封装 • 应用场景:适用于车载充电器、电动车仪表、电信基站电源、电源适配器等 性能特点 • 输入电压范围:9.2V至95V • 输出电流:可提供4.5A连续负载电流 • 效率:高…

【C++】B2108 图像模糊处理

博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 💯前言💯题目描述题目内容输入格式输出格式示例输入:输出: 💯题目分析问题拆解 💯我的做法代码实现代码分析 💯老师的做法…

怎么把word试题转成excel?

在教育行业、学校管理以及在线学习平台中,试题库的高效管理是一项核心任务。许多教育工作者和系统开发人员常常面临将 Word 中的试题批量导入 Excel 的需求。本文将详细介绍如何快速将试题从 Word 转换为 Excel,帮助您轻松解决繁琐的数据整理问题&#x…

minibatch时,损失如何记录

目录 minibatch时,损失如何记录 报错:UnboundLocalError: local variable coef referenced before assignment是什么回事 未溢出则不会报错,可以完整滴运行完成 indent 缩进 炫酷技能:一遍运行,一遍画图 实例1 解释…

Linux : Linux环境开发工具vim / gcc / makefile / gdb / git的使用

Linux环境开发工具的使用 一、操作系统的生态二、程序下载安装(一)程序安装方式(二)包管理器 yum / apt 运行原理 三、文本编辑器 vim(一)认识vim 下的操作模式(二)命令模式常用的快…

国产游戏崛起,燕云十六移动端1.9上线,ToDesk云电脑先开玩

游戏爱好者的利好消息出新了!网易大型武侠仙游《燕云十六声》正式官宣,移动端要在1月9日正式上线了!你期待手游版的燕云吗?不妨评论区留言说说你的看法。小编分别花了几个小时在台式机电脑和手机上都试了下,欣赏画面还…

力扣刷题:数组OJ篇(下)

大家好,这里是小编的博客频道 小编的博客:就爱学编程 很高兴在CSDN这个大家庭与大家相识,希望能在这里与大家共同进步,共同收获更好的自己!!! 目录 1.轮转数组(1)题目描述…

有序数据中插入不确定数据保证数据插入的位置顺序正确排序

解决有序数据中插入不确定数据保证数据插入的位置顺序正确排序 前言 java 数据库中存储自增id 有序的数据, 前端页面基于 id 5和 6 之间新增一条数据,在 id 6 和 7之间新增 2条,或者更复杂的场景,后台接口如何保存数据使得页面数…

python无需验证码免登录12306抢票 --selenium(2)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 [TOC](python无需验证码免登录12306抢票 --selenium(2)) 前言 提示:这里可以添加本文要记录的大概内容: 就在刚刚我抢的票:2025年1月8日…

DNS协议漏洞利用实验_hust计算机网络安全实验

文章目录 计算机网络安全实验 DNS协议漏洞利用实验 docker使用 建立实验环境docker常用指令 一些注意事项设置本地 DNS 服务器 配置用户计算机设置本地DNS服务器在本地 DNS 服务器中建一个区域 修改主机文件(可略)netwox实施DNS的用户响应欺骗攻击netwo…

基于MP157AAA的I2C练习

练习要求: 通过I2C分别实现与芯片si7006(获取湿度、温度)和芯片ap3216(获取环境光照强度)的通讯; 1、运行效果 2、分析ap3216如何获取光照强度 2.1、需要操作的寄存器 通过分析手册,需要操作以下寄存器: 0x00:系统配置 0x0C&…

【Linux】深入理解文件系统(超详细)

目录 一.磁盘 1-1 磁盘、服务器、机柜、机房 📌补充: 📌通常网络中用高低电平,磁盘中用磁化方向来表示。以下是具体说明: 📌如果有一块磁盘要进行销毁该怎么办? 1-2 磁盘存储结构 ​编辑…

网络安全图谱以及溯源算法

​ 本文提出了一种网络攻击溯源框架&#xff0c;以及一种网络安全知识图谱&#xff0c;该图由六个部分组成&#xff0c;G <H&#xff0c;V&#xff0c;A&#xff0c;E&#xff0c;L&#xff0c;S&#xff0c;R>。 1|11.知识图 ​ 网络知识图由六个部分组成&#xff0c…

《Spring Framework实战》7:4.1.2.容器概述

欢迎观看《Spring Framework实战》视频教程 容器概述 该接口表示 Spring IoC 容器&#xff0c;并负责实例化、配置和组装 bean。 容器在组件上获取其指令&#xff0c;以实例化、配置和 通过读取配置元数据进行汇编。可以表示配置元数据 作为带注释的组件类、具有工厂方法的配置…

学生公寓技术规格书如何编写?

学生公寓限电柜的技术规格书主要包括以下内容‌&#xff1a; ‌用电计量计费‌&#xff1a;限电柜可以通过计算机售电管理系统进行用电计量计费&#xff0c;学生需要预交电费&#xff0c;系统会自动将数据传给控电柜和配电箱&#xff0c;对宿舍的电量进行累减计量‌。 ‌时间控…

【HarmonyOS NEXT】鸿蒙应用点9图的处理(draw9patch)

【HarmonyOS NEXT】鸿蒙应用点9图的处理&#xff08;draw9patch&#xff09; 一、前言&#xff1a; 首先在鸿蒙中是不支持安卓 .9图的图片直接使用。只有类似拉伸的处理方案&#xff0c;鸿蒙提供的Image组件有与点九图相同功能的API设置。 可以通过设置resizable属性来设置R…

SpringBoot 使用 Cache 集成 Redis做缓存保姆教程

1. 项目背景 Spring Cache是Spring框架提供的一个缓存抽象层&#xff0c;它简化了缓存的使用和管理。Spring Cache默认使用服务器内存&#xff0c;并无法控制缓存时长&#xff0c;查找缓存中的数据比较麻烦。 因此Spring Cache支持将缓存数据集成到各种缓存中间件中。本文已常…

MySQL事件功能简介

MySQL 的事件调度器&#xff08;Event Scheduler&#xff09;提供了一种便捷的方法来定时执行 SQL 语句&#xff0c;从而实现数据维护、报告生成等自动化操作。本文将详细介绍 MySQL 的事件功能&#xff0c;并说明如何使用 Navicat 管理这些事件。 1. 什么是 MySQL 事件调度器…

高光谱相机的特点

光谱特性 高光谱分辨率&#xff1a;能将光谱范围分割成极窄的波段&#xff0c;光谱分辨率通常达到纳米级甚至亚纳米级&#xff0c;可精确捕捉到不同物质在细微光谱差异上的特征&#xff0c;比如可以区分不同种类的植被因叶绿素含量等差异而在光谱上的细微变化。 多波段探测&a…

备考蓝桥杯:数据结构概念浅谈

目录 1数据结构的概念 什么是数据结构: 为什么要有数据结构 2.数据结构的三个组成要素 1.逻辑结构 2.存储结构 3.数据运算 3。算法好坏的度量&#xff08;时间复杂度和空间复杂度&#xff09; 时间复杂度计算 最优和平均和最差时间复杂度 计算时间复杂度例子 空间复…