Spring 魔法探秘:从 Bean 线程安全到事务魔法全解析
1.Spring 框架中的单例 Bean 是线程安全的么?
Spring 框架中的单例 Bean 本身并不保证线程安全性。单例模式意味着在整个应用程序的生命周期中,只会创建该 Bean 的一个实例,并且所有对该 Bean 的请求都将共享这个实例。
线程安全与否取决于 Bean 的设计和使用方式:
- 如果单例 Bean 只包含不可变的状态(即没有可变成员变量),或者其成员变量在初始化后不再改变,那么这个 Bean 就是线程安全的,因为不会出现多个线程同时修改状态的情况。
- 如果单例 Bean 包含可变状态并且这些状态可能会被多个线程并发访问和修改,那么开发者需要确保适当的同步机制来保护这些状态。这可以通过多种方式实现,例如使用同步方法或块、原子类型、volatile 关键字、并发集合类等。
- Spring 还提供了一些特性来帮助管理线程安全问题,比如作用域为 prototype 的 Bean(每次注入都会创建新的实例)、基于 ThreadLocal 的 Bean(每个线程都有自己的 Bean 实例副本)以及利用 AOP 来添加事务管理等横切关注点,以间接支持线程安全。
- 对于无状态的服务 Bean(如 DAO 或者服务层 Bean),它们通常不持有任何会话或对话级别的状态,因此通常是线程安全的。
总之,在开发时需要考虑单例 Bean 的线程安全性,尤其是在多线程环境下,如 Web 应用程序中。如果需要共享可变状态,应该采取适当的措施来保证线程安全。
2.Spring 是怎么解决循环依赖的?
Spring 框架在处理循环依赖时,采用了多种策略来确保依赖注入能够正常工作。循环依赖指的是两个或多个 Bean 之间直接或间接地相互依赖。例如,Bean A 依赖于 Bean B,而 Bean B 又依赖于 Bean A。
Spring 解决循环依赖的主要机制如下:
-
单例作用域的 Bean:对于单例作用域的 Bean,默认情况下 Spring 容器使用三级缓存来解决循环依赖的问题:
- 一级缓存(singletonObjects):用于存储已经完全初始化完成的单例 Bean。
- 二级缓存(earlySingletonObjects):用于存储尚未完全初始化但已经完成了实例化和属性填充的单例 Bean 的早期暴露对象。
- 三级缓存(singletonFactories):用于存储原始工厂对象,这些对象可以用来获取二级缓存中的早期暴露对象。
当 Spring 容器检测到一个循环依赖时,它会尝试从二级缓存中获取尚未完全初始化的 Bean,并将其提供给需要它的其他 Bean 使用。一旦该 Bean 完全初始化后,就会被移到一级缓存中。
- 构造器注入与设值注入:Spring 默认支持设值注入(通过 Setter 方法),并且对构造器注入也有很好的支持。当使用构造器注入时,如果存在循环依赖,Spring 将无法解析这种依赖关系,因为构造器注入要求所有依赖项都必须在创建 Bean 实例之前准备好。因此,对于可能存在循环依赖的情况,推荐使用设值注入。
- 自动装配延迟:Spring 还提供了 @Lazy 注解,它可以延迟 Bean 的初始化直到第一次被请求。这可以帮助避免某些类型的循环依赖问题,尤其是在应用启动时并非所有的 Bean 都立即需要的情况下。
- 自定义解决方案:在一些复杂场景下,开发者可能需要自定义逻辑来解决循环依赖问题,比如重构代码以消除循环依赖,或者使用其他设计模式如观察者模式等。
需要注意的是,虽然 Spring 提供了上述机制来处理循环依赖,但这并不意味着应该鼓励编写存在循环依赖的代码。良好的面向对象设计通常应尽量避免这种情况的发生,因为它可能会导致代码难以理解和维护。如果确实遇到了不可避免的循环依赖,那么应该仔细评估并考虑是否可以通过重构或其他方式来优化代码结构。
3.说说事务的隔离级别
数据库事务的隔离级别是用来控制一个事务对其他并发事务的可见性和干扰程度,确保数据的一致性。SQL 标准定义了四种隔离级别,每一种都防止了一组特定的问题:脏读(Dirty Read)、不可重复读(Non-repeatable Read)和幻读(Phantom Read)。这四个隔离级别从低到高分别是:
-
读未提交(Read Uncommitted)
- 这是最低的隔离级别,在这个级别下,一个事务可以读取另一个尚未提交事务的数据变更。这意味着可能会发生脏读、不可重复读和幻读。
- 在大多数数据库系统中,这种隔离级别很少被使用,因为它可能导致非常不一致的视图。
-
读已提交(Read Committed)
- 在此隔离级别下,一个事务只能读取另一个已经提交事务所做的更改。它可以防止脏读,但是不可重复读和幻读仍然可能发生。
- 这是许多数据库系统的默认隔离级别,例如 SQL Server 和 Oracle。
-
可重复读(Repeatable Read)
- 在这个级别,只要事务开始,它就能保证在该事务内多次读取同一数据的结果是一致的,即防止了不可重复读。然而,幻读仍然可能发生在新的记录插入时。
- MySQL 的 InnoDB 存储引擎默认使用此隔离级别。
-
序列化(Serializable)
- 这是最高的隔离级别,完全串行化事务执行,确保了最高的数据一致性和完整性。在这个级别下,所有事务依次执行,避免了脏读、不可重复读以及幻读。
- 序列化通常通过锁定表或行来实现,这可能会显著降低并发性能,因此只在确实需要最高级别的数据一致性时使用。
选择合适的隔离级别取决于应用的具体需求,包括对数据一致性的要求、并发性能考虑等因素。较高的隔离级别提供了更强的数据保护,但可能会导致更多的锁竞争和较低的并发度;而较低的隔离级别则提高了并发处理能力,但也增加了数据不一致的风险。开发者应该根据实际情况权衡利弊,选取最适合的隔离级别。
4.说说事务的传播级别
在 Spring 框架中,事务传播行为(Propagation Behavior)定义了当一个方法被调用时,如何与当前存在的事务进行交互。这决定了是加入现有事务、创建新事务还是以非事务方式执行等行为。Spring 提供了多种传播行为来满足不同的需求。以下是主要的传播级别及其含义:
- PROPAGATION_REQUIRED (默认)
- 如果当前存在事务,则加入该事务;如果不存在,则创建一个新的事务。
- 这是最常用的传播行为。
- PROPAGATION_SUPPORTS
- 如果当前存在事务,则加入该事务;如果不存在,则以非事务方式执行。
- 适用于读取操作,这些操作可以受益于事务的存在,但也可以在没有事务的情况下安全地运行。
- PROPAGATION_MANDATORY
- 如果当前存在事务,则加入该事务;如果不存在,则抛出异常。
- 当你确定某个方法应该总是在事务上下文中执行时使用。
- PROPAGATION_REQUIRES_NEW
- 总是创建一个新的事务。如果当前存在事务,则将其挂起。
- 用于需要完全独立事务的操作,比如记录日志或写入审计信息,确保即使外部事务失败也不会影响到这个新的事务。
- PROPAGATION_NOT_SUPPORTED
- 以非事务方式执行,并且如果当前存在事务,则将其挂起。
- 对那些不需要事务支持的操作非常有用,可以避免不必要的事务开销。
- PROPAGATION_NEVER
- 以非事务方式执行,如果当前存在事务,则抛出异常。
- 在你绝对肯定某个方法不应该参与任何事务时使用。
- PROPAGATION_NESTED
- 如果当前存在事务,则在嵌套事务内执行;如果不存在,则表现如同 PROPAGATION_REQUIRED。
- 嵌套事务意味着它是一个内部事务,可以独立于外部事务提交或回滚。但是,这依赖于底层数据库和驱动的支持,不是所有数据库都支持这种特性。
选择正确的事务传播行为对于构建高效、可靠的分布式系统非常重要。不同的传播行为适合不同类型的业务逻辑,开发者应当根据实际应用场景来决定最适合的传播级别。例如,在处理金融交易等对数据一致性要求极高的场景中,可能更倾向于使用 PROPAGATION_REQUIRED 或 PROPAGATION_REQUIRES_NEW,而在一些查询类操作中,PROPAGATION_SUPPORTS 或 PROPAGATION_NOT_SUPPORTED 可能更为合适。
5.Spring事务实现方式
Spring 框架提供了两种主要的事务管理方式:编程式事务管理和声明式事务管理。每种方式都有其特点和适用场景。
编程式事务管理
编程式事务管理通过编写代码来显式地控制事务的边界,即何时开始、提交或回滚事务。这种方式给予了开发者完全的控制权,但通常认为它不够优雅,因为事务管理逻辑与业务逻辑紧密耦合在一起,增加了代码复杂度。在 Spring 中,可以使用 TransactionTemplate 或者直接使用底层的 PlatformTransactionManager 接口来进行编程式事务管理。
优点:
- 提供了对事务更细粒度的控制。
缺点:
- 需要在每个需要事务的地方手动编码,容易导致代码重复和维护困难。
- 增加了业务逻辑的复杂性。
示例代码(使用 TransactionTemplate):
@Autowired
private TransactionTemplate transactionTemplate;public void executeInTransaction() {transactionTemplate.execute(status -> {// 事务中的业务逻辑return null;});
}
声明式事务管理
声明式事务管理是基于 AOP(面向切面编程)的,允许你将事务管理逻辑从业务逻辑中分离出来。通过配置或注解的方式指定哪些方法应该运行在事务上下文中,从而简化了代码并提高了可读性和可维护性。这是 Spring 推荐使用的事务管理方式。
优点:
- 简化了事务管理代码,减少了重复代码。
- 使得业务逻辑更加清晰,专注于核心功能而非事务处理细节。
缺点:
- 对于复杂的事务需求,灵活性可能不如编程式事务管理。
- 使用 XML 配置
- 在旧版本的 Spring 中,可以通过 XML 文件配置事务管理器和代理工厂来实现声明式事务管理。
使用注解
现代开发中,更常见的是使用 @Transactional 注解来标记需要事务管理的方法或类。这使得配置变得更加简洁,并且易于理解和维护。
示例代码(使用 @Transactional 注解):
@Service
public class MyService {@Transactionalpublic void performSomeBusinessLogic() {// 事务中的业务逻辑}
}
在这个例子中,performSomeBusinessLogic 方法会在一个事务中执行。如果该方法抛出了未捕获的异常,默认情况下事务会被标记为回滚状态;否则,事务将在方法正常结束时被提交。
总结
Spring 的事务管理机制非常灵活,支持多种实现方式。对于大多数应用场景而言,声明式事务管理(尤其是基于注解的方式)是最常用的选择,因为它能够有效地减少代码量并且保持良好的可维护性。然而,在某些特殊情况下,编程式事务管理也能提供必要的灵活性。选择哪种方式取决于具体的应用需求和技术偏好。
相关文章:
Spring 魔法探秘:从 Bean 线程安全到事务魔法全解析
1.Spring 框架中的单例 Bean 是线程安全的么? Spring 框架中的单例 Bean 本身并不保证线程安全性。单例模式意味着在整个应用程序的生命周期中,只会创建该 Bean 的一个实例,并且所有对该 Bean 的请求都将共享这个实例。 线程安全与否取决于…...

[Maven]IDEA父工程创建子工程后父工程不可运行
IDEA在使用maven构建项目时,如果你在当前工程下创建一个子工程,那么原有的工程(变为父工程的工程)原有的代码通常会变得不可运行。 这是因为,使用maven创建父子工程关系后,IDEA会自动变更项目的模块相关配置。 比如这是我maven工程…...

【系统移植】在开发板上加载内核和根文件系统的三种方法
实现环境:ubuntu24.04和FS4412实验平台。 要在开发板上运行linux操作系统,首先要将linux内核镜像(uImage)、设备树(dexynos4412-fs4412.dtb)和根文件系统镜像(ramdisk.img)加载到开发板内存。有以下几种方式加载: 一、通过tftp加载内核和根文件系统 二、通过EMMC加…...

#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍02-基于错误消息的SQL注入(Error-Based SQL Injection)
免责声明 本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停…...

数据结构-排序(来自于王道)
排序的基本概念 插入排序 在这个算法中,除了输入的数组本身,没有使用额外的数据结构来存储数据,所有的操作都是在原数组上进行的。因此,无论输入数组的大小 n 是多少,算法执行过程中所占用的额外空间是固定的ÿ…...

【蓝桥杯选拔赛真题93】Scratch青蛙过河 第十五届蓝桥杯scratch图形化编程 少儿编程创意编程选拔赛真题解析
目录 Scratch青蛙过河 一、题目要求 编程实现 二、案例分析 1、角色分析 2、背景分析 3、前期准备 三、解题思路 1、思路分析 2、详细过程 四、程序编写 五、考点分析 六、推荐资料 1、入门基础 2、蓝桥杯比赛 3、考级资料 4、视频课程 5、python资料 Scr…...

ReactPress最佳实践—搭建导航网站实战
Github项目地址:https://github.com/fecommunity/easy-blog 欢迎Star。 近期,阮一峰在科技爱好者周刊第 325 期中推荐了一款开源工具——ReactPress,ReactPress一个基于 Next.js 的博客和 CMS 系统,可查看 demo站点。(…...

Hive-4.0.1数据库搭建(可选配置用户名密码远程连接)
1.官网下载tar包上传到服务器并解压(我这里解压到了hive目录): 2.进入到conf目录,并复制模板配置文件进行修改: cd /apache-hive-4.0.1-bin/conf cp hive-default.xml.template hive-site.xml3.编写内容如下: <property>&…...

P8772 求和 P8716 回文日期
文章目录 [蓝桥杯 2022 省 A] 求和[蓝桥杯 2020 省 AB2] 回文日期 [蓝桥杯 2022 省 A] 求和 题目描述 给定 n n n 个整数 a 1 , a 2 , ⋯ , a n a_{1}, a_{2}, \cdots, a_{n} a1,a2,⋯,an, 求它们两两相乘再相加的和,即 S a 1 ⋅ a 2 a 1 ⋅ a 3 ⋯ a…...
MySQL迁移SQLite
将 MySQL 的表结构和数据迁移到 SQLite,可以通过以下步骤实现。这个过程主要包括导出 MySQL 数据库到 SQL 文件,然后将其导入到 SQLite 数据库中。 步骤 1: 导出 MySQL 数据库 首先,需要将 MySQL 数据库导出为一个 SQL 文件。可以使用 mysq…...

RocketMQ中的顺序消息和乱序消息详解
内容编辑中… 1.背景 顺序消息是消息队列 RocketMQ 提供的一种高级消息类型。 对于一个指定的Topic,消息严格按照先进先出(FIFO)的原则进行消息发布和消费。 即先发送的消息先消费,后发送的消息后消费。 顺序消息在发送、存储和投递的处理过程中,强调多条消息间的先后…...

Unity UGUI图片循环列表插件
效果展示: 下载链接:https://gf.bilibili.com/item/detail/1111843026 概述: LoopListView2 是一个与 UGUI ScrollRect 相同的游戏对象的组件。它可以帮助 UGUI ScrollRect 以高效率和节省内存的方式支持任意数量的项目。 对于具有10,000个…...

Kafka系列教程 - Kafka 生产者 -2
1. 生产者简介 不管是把 Kafka 作为消息队列系统、还是数据存储平台,总是需要一个可以向 Kafka 写入数据的生产者和一个可以从 Kafka 读取数据的消费者,或者是一个兼具两种角色的应用程序。 使用 Kafka 的场景很多,诉求也各有不同ÿ…...

AI Chat API 对接说明
AI Chat API 对接说明 我们知道,市面上一些问答 API 的对接还是相对没那么容易的,比如说 OpenAI 的 Chat Completions API,它有一个 messages 字段,如果要完成连续对话,需要我们把所有的上下文历史全部传递࿰…...
Thread线程基础使用
多线程目的:其实就是希望“并行”执行多任务,提升效率。 单核多线程基于时间片轮询 并发而非并行 线程最大数等于cpu核心数为佳 namespace thinger.ThreadDemo {class Program{//主线程static void Main(string[] args){Console.WriteLine("这个…...

【Linux】结构化命令
结构化命令structured command:允许脚本根据条件跳过部分命令,改变执行流程。 1、if-then语句 格式1: if command then commands fi 格式2: if command; then commands fi 运行if之后的command命令,如果它的退出状态码…...

ElasticSearch01-概述
零、文章目录 ElasticSearch01-概述 1、Elastic Stack (1)简介 官网地址:https://www.elastic.co/cn/ELK是一个免费开源的日志分析架构技术栈总称,包含三大基础组件,分别是Elasticsearch、Logstash、Kibana。但实际…...
docker xxxx is using its referenced image ea06665f255d
Error response from daemon: conflict: unable to remove repository reference “registrxxxxxx” (must force) - container 9642fd1fd4a0 is using its referenced image ea06665f255d 这个错误表明你尝试删除的镜像正在被一个容器使用,因此无法删除。要解决这…...
Vue 2 中 v-text 和 v-html 指令的使用详解
目录 Vue 2 中 v-text 和 v-html 指令的使用详解 v-text 指令 简介 基本语法 示例 1:基础用法 特点 v-html 指令 简介 基本语法 示例 2:基础用法 注意事项 区别与选择指南 何时使用 最佳实践 Vue 2 中 v-text 和 v-html 指令的使用详解 V…...
高级Python游戏开发:创建一款多人对战坦克大战
在本教程中,我们将用Python的Pygame库开发一款高级的坦克大战游戏。这款游戏支持多人对战、碰撞检测、子弹射击以及地图障碍生成,适合作为学习Python高级游戏开发的练习项目。 一、游戏功能概述 多人对战模式:玩家可以操作坦克,在同一屏幕上互相攻击。子弹射击:坦克可以发…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...
在 Spring Boot 项目里,MYSQL中json类型字段使用
前言: 因为程序特殊需求导致,需要mysql数据库存储json类型数据,因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...