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高级游戏开发的练习项目。 一、游戏功能概述 多人对战模式:玩家可以操作坦克,在同一屏幕上互相攻击。子弹射击:坦克可以发…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...
算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...
保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!
目录 补间动画 1.创建资源文件夹 2.设置文件夹类型 3.创建.xml文件 4.样式设计 5.动画设置 6.动画的实现 内容拓展 7.在原基础上继续添加.xml文件 8.xml代码编写 (1)rotate_anim (2)scale_anim (3)translate_anim 9.MainActivity.java代码汇总 10.效果展示 逐帧…...
Android写一个捕获全局异常的工具类
项目开发和实际运行过程中难免会遇到异常发生,系统提供了一个可以捕获全局异常的工具Uncaughtexceptionhandler,它是Thread的子类(就是package java.lang;里线程的Thread)。本文将利用它将设备信息、报错信息以及错误的发生时间都…...
