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

Spring事务基础知识+使用方法+源码拆解

下面我会简单介绍一下 Spring 事务的基础知识以及使用方法然后直接对源码进行拆解。不 BB上文章目录。1.1. 项目准备需要搭建环境的同学代码详见https://github.com/lml200701158/program_demo/tree/main/spring-transaction下面是 DB 数据和 DB 操作接口uidunameusex1张三女2陈恒男3楼仔男// 提供的接口 public interface UserDao { // select * from user_test where uid #{uid} public MyUser selectUserById(Integer uid); // update user_test set uname #{uname},usex #{usex} where uid #{uid} public int updateUser(MyUser user); }基础测试代码testSuccess() 是事务生效的情况Service public class Louzai { Autowired private UserDao userDao; public void update(Integer id) { MyUser user new MyUser(); user.setUid(id); user.setUname(张三-testing); user.setUsex(女); userDao.updateUser(user); } public MyUser query(Integer id) { MyUser user userDao.selectUserById(id); return user; } // 正常情况 Transactional(rollbackFor Exception.class) public void testSuccess() throws Exception { Integer id 1; MyUser user query(id); System.out.println(原记录: user); update(id); throw new Exception(事务生效); } }执行入口public class SpringMyBatisTest { public static void main(String[] args) throws Exception { String xmlPath applicationContext.xml; ApplicationContext applicationContext new ClassPathXmlApplicationContext(xmlPath); Louzai uc (Louzai) applicationContext.getBean(louzai); uc.testSuccess(); } }输出16:44:38.267 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean org.springframework.transaction.interceptor.TransactionInterceptor#0 16:44:38.363 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean txManager 16:44:40.966 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Creating new transaction with name [com.mybatis.controller.Louzai.testSuccess]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.Exception 16:44:40.968 [main] DEBUG org.springframework.jdbc.datasource.DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/java_study?useUnicodetruecharacterEncodingUTF-8useSSLfalseserverTimezoneAsia/Shanghai] 16:44:41.228 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl5b5caf08] for JDBC transaction 16:44:41.231 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl5b5caf08] to manual commit 原记录:MyUser(uid1, uname张三, usex女) 16:42:59.345 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Initiating transaction rollback 16:42:59.346 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Rolling back JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl70807224] 16:42:59.354 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl70807224] after transaction Exception in thread main java.lang.Exception: 事务生效 at com.mybatis.controller.Louzai.testSuccess(Louzai.java:34) // 异常日志省略...2. Spring 事务工作流程为了方便大家能更好看懂后面的源码我先整体介绍一下源码的执行流程让大家有一个整体的认识否则容易被绕进去。整个 Spring 事务源码其实分为 2 块我们会结合上面的示例给大家进行讲解。第一块是后置处理我们在创建 Louzai Bean 的后置处理器中里面会做两件事情获取 Louzai 的切面方法首先会拿到所有的切面信息和 Louzai 的所有方法进行匹配然后找到 Louzai 所有需要进行事务处理的方法匹配成功的方法还需要将事务属性保存到缓存 attributeCache 中。创建 AOP 代理对象结合 Louzai 需要进行 AOP 的方法选择 Cglib 或 JDK创建 AOP 代理对象。第二块是事务执行整个逻辑比较复杂我只选取 4 块最核心的逻辑分别为从缓存拿到事务属性、创建并开启事务、执行业务逻辑、提交或者回滚事务。3. 源码解读注意Spring 的版本是 5.2.15.RELEASE否则和我的代码不一样上面的知识都不难下面才是我们的重头戏让你跟着楼仔走一遍代码流程。3.1 代码入口这里需要多跑几次把前面的 beanName 跳过去只看 louzai。进入 doGetBean()进入创建 Bean 的逻辑。进入 createBean()调用 doCreateBean()。进入 doCreateBean()调用 initializeBean()。如果看过我前面几期系列源码的同学对这个入口应该会非常熟悉其实就是用来创建代理对象。3.2 创建代理对象这里是重点敲黑板先获取 louzai 类的所有切面列表创建一个 AOP 的代理对象。3.2.1 获取切面列表这里有 2 个重要的方法先执行 findCandidateAdvisors()待会我们还会再返回 findEligibleAdvisors()。依次返回重新来到 findEligibleAdvisors()。进入 canApply()开始匹配 louzai 的切面。这里是重点敲黑板这里只会匹配到 Louzai.testSuccess() 方法我们直接进入匹配逻辑。如果匹配成功还会把事务的属性配置信息放入 attributeCache 缓存。我们依次返回到 getTransactionAttribute()再看看放入缓存中的数据。再回到该小节开头我们拿到 louzai 的切面信息去创建 AOP 代理对象。3.2.2 创建 AOP 代理对象创建 AOP 代理对象的逻辑在上一篇文章Spring AOP讲解过我是通过 Cglib 创建感兴趣的同学可以关注公众号「楼仔」翻一下楼仔的历史文章。3.3 事务执行回到业务逻辑通过louzai 的 AOP 代理对象开始执行主方法。因为代理对象是 Cglib 方式创建所以通过 Cglib 来执行。这里是重点敲黑板下面的代码是事务执行的核心逻辑 invokeWithinTransaction()。protected Object invokeWithinTransaction(Method method, Nullable Class? targetClass, final InvocationCallback invocation) throws Throwable { //获取我们的事务属源对象 TransactionAttributeSource tas getTransactionAttributeSource(); //通过事务属性源对象获取到我们的事务属性信息 final TransactionAttribute txAttr (tas ! null ? tas.getTransactionAttribute(method, targetClass) : null); //获取我们配置的事务管理器对象 final PlatformTransactionManager tm determineTransactionManager(txAttr); //从tx属性对象中获取出标注了Transactionl的方法描述符 final String joinpointIdentification methodIdentification(method, targetClass, txAttr); //处理声明式事务 if (txAttr null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) { //有没有必要创建事务 TransactionInfo txInfo createTransactionIfNecessary(tm, txAttr, joinpointIdentification); Object retVal; try { //调用钩子函数进行回调目标方法 retVal invocation.proceedWithInvocation(); } catch (Throwable ex) { //抛出异常进行回滚处理 completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { //清空我们的线程变量中transactionInfo的值 cleanupTransactionInfo(txInfo); } //提交事务 commitTransactionAfterReturning(txInfo); return retVal; } //编程式事务 else { // 这里不是我们的重点省略... } }3.3.1 获取事务属性在 invokeWithinTransaction() 中我们找到获取事务属性的入口。从 attributeCache 获取事务的缓存数据缓存数据是在 “2.2.1 获取切面列表” 中保存的。3.3.2 创建事务通过 doGetTransaction() 获取事务。protected Object doGetTransaction() { //创建一个数据源事务对象 DataSourceTransactionObject txObject new DataSourceTransactionObject(); //是否允许当前事务设置保持点 txObject.setSavepointAllowed(isNestedTransactionAllowed()); /** * TransactionSynchronizationManager 事务同步管理器对象(该类中都是局部线程变量) * 用来保存当前事务的信息,我们第一次从这里去线程变量中获取 事务连接持有器对象 通过数据源为key去获取 * 由于第一次进来开始事务 我们的事务同步管理器中没有被存放.所以此时获取出来的conHolder为null */ ConnectionHolder conHolder (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource()); txObject.setConnectionHolder(conHolder, false); //返回事务对象 return txObject; }通过 startTransaction() 开启事务。下面是开启事务的详细逻辑了解一下即可。protected void doBegin(Object transaction, TransactionDefinition definition) { //强制转化事务对象 DataSourceTransactionObject txObject (DataSourceTransactionObject) transaction; Connection con null; try { //判断事务对象没有数据库连接持有器 if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) { //通过数据源获取一个数据库连接对象 Connection newCon obtainDataSource().getConnection(); if (logger.isDebugEnabled()) { logger.debug(Acquired Connection [ newCon ] for JDBC transaction); } //把我们的数据库连接包装成一个ConnectionHolder对象 然后设置到我们的txObject对象中去 txObject.setConnectionHolder(new ConnectionHolder(newCon), true); } //标记当前的连接是一个同步事务 txObject.getConnectionHolder().setSynchronizedWithTransaction(true); con txObject.getConnectionHolder().getConnection(); //为当前的事务设置隔离级别 Integer previousIsolationLevel DataSourceUtils.prepareConnectionForTransaction(con, definition); txObject.setPreviousIsolationLevel(previousIsolationLevel); //关闭自动提交 if (con.getAutoCommit()) { txObject.setMustRestoreAutoCommit(true); if (logger.isDebugEnabled()) { logger.debug(Switching JDBC Connection [ con ] to manual commit); } con.setAutoCommit(false); } //判断事务为只读事务 prepareTransactionalConnection(con, definition); //设置事务激活 txObject.getConnectionHolder().setTransactionActive(true); //设置事务超时时间 int timeout determineTimeout(definition); if (timeout ! TransactionDefinition.TIMEOUT_DEFAULT) { txObject.getConnectionHolder().setTimeoutInSeconds(timeout); } // 绑定我们的数据源和连接到我们的同步管理器上 把数据源作为key,数据库连接作为value 设置到线程变量中 if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder()); } } catch (Throwable ex) { if (txObject.isNewConnectionHolder()) { //释放数据库连接 DataSourceUtils.releaseConnection(con, obtainDataSource()); txObject.setConnectionHolder(null, false); } throw new CannotCreateTransactionException(Could not open JDBC Connection for transaction, ex); } }最后返回到 invokeWithinTransaction()得到 txInfo 对象。3.3.3 执行逻辑还是在 invokeWithinTransaction() 中开始执行业务逻辑。进入到真正的业务逻辑。执行完毕后抛出异常依次返回走后续的回滚事务逻辑。3.3.4 回滚事务还是在 invokeWithinTransaction() 中进入回滚事务的逻辑。。执行回滚逻辑很简单我们只看如何判断是否回滚。如果抛出的异常类型和事务定义的异常类型匹配证明该异常需要捕获。之所以用递归不仅需要判断抛出异常的本身还需要判断它继承的父类异常满足任意一个即可捕获。到这里所有的流程结束。4. 结语我们再小节一下文章先介绍了事务的使用示例以及事务的执行流程。之后再剖析了事务的源码分为 2 块先匹配出 louzai 对象所有关于事务的切面列表并将匹配成功的事务属性保存到缓存从缓存取出事务属性然后创建、启动事务执行业务逻辑最后提交或者回滚事务。

相关文章:

Spring事务基础知识+使用方法+源码拆解

下面我会简单介绍一下 Spring 事务的基础知识,以及使用方法,然后直接对源码进行拆解。 不 BB,上文章目录。 1. 1. 项目准备 需要搭建环境的同学,代码详见:https://github.com/lml200701158/program_demo/tree/main/s…...

Unity游戏接入Steam成就系统:从Steamworks配置到C# API调用的保姆级避坑指南

Unity游戏接入Steam成就系统全流程实战指南 当独立游戏开发者决定将作品发布到Steam平台时,成就系统往往是提升玩家留存和互动的重要功能。不同于简单的API调用,一个健壮的Steam成就实现需要前后端配置、统计逻辑绑定和代码架构的完整配合。本文将带你从…...

2026年AI企业怎么选?深度评测解析,新手也能精准避坑

一、摘要据IDC 2026年上半年中国AI行业发展报告显示,国内AI企业数量突破3000家,但具备核心技术自研能力、可实现规模化商业落地的企业不足20%,同质化竞争导致用户选型失误率高达45%。无论是企业用户寻求AI解决方案赋能业务,还是个…...

YOLOv8在Jetson上实时推理的终极优化:从.pt到INT8/FP16量化TensorRT引擎全流程

YOLOv8在Jetson平台上的极致性能优化:从模型量化到TensorRT部署实战 当你在Jetson边缘设备上部署YOLOv8模型时,是否遇到过这样的困境——明明使用了GPU加速,推理速度却依然无法满足实时视频分析的需求?这可能是由于你没有充分利用…...

C99与C11模式下解决for循环初始化声明错误的实用指南

1. 为什么你的for循环会报错? 最近有个朋友在用CodeBlocks写C代码时遇到了一个奇怪的错误。他在for循环里声明变量时,编译器直接报错:"[Error] for loop initial declarations are only allowed in C99 or C11 mode"。这让他很困惑…...

天尊传奇「剑神密藏」全攻略

核心玩法:消耗灵符 / 剑神密钥抽奖,享高倍加成,可 100 连抽,目标拿飞剑、神装等稀缺战力资源天尊传奇。一、核心规则与折扣消耗:灵符或剑神密钥(密钥可通过活动 / 商城获取,优先用密钥保底&…...

课题申请:如何预判评审潜台词并从容应对?

在基金申报的激烈竞争中,许多科研人员常常陷入一个误区:将申请书单纯地视为一份“任务说明书”。我们习惯于在文中详细罗列“要做什么”、“打算怎么做”,却往往忽略了评审专家在阅读时的心理活动。当一份申请书只停留在陈述层面,…...

【爬虫实战对比】Requests vs Scrapy 笔趣阁小说爬虫,从单线程到高效并发的全方位升级

【爬虫实战对比】Requests vs Scrapy 笔趣阁小说爬虫,从单线程到高效并发的全方位升级 近期完成了笔趣阁小说爬虫的重构,从最初的Requests单线程版本,升级为Scrapy框架版本,过程中深刻体会到两者在开发效率、运行性能、代码可维护…...

1644万,无锡市“一网统管”城市运行管理平台

4月3日,无锡市“一网统管”城市运行管理平台(扩续建2025)采购公告,项目预算金额:1644.439万元,提交投标文件截止时间:2026-04-29 09:30 (北京时间)。一、项目信息&#x…...

智元GO-2:具身基座大模型新突破

智元机器人正式推出新一代具身基座大模型Genie Operator-2(GO-2),它在GO-1基础上进化,弥合语义‑运动鸿沟,在多个基准测试中刷新行业SOTA。进化亮点:弥合语义‑运动鸿沟GO-2在GO-1基础上进化,致…...

Qwen-Ranker Pro效果展示:‘猫洗澡’vs‘狗洗澡’语义陷阱精准识别案例

Qwen-Ranker Pro效果展示:‘猫洗澡’vs‘狗洗澡’语义陷阱精准识别案例 1. 引言:当搜索遇到语义陷阱 你有没有遇到过这样的情况:在搜索引擎中输入"猫洗澡的注意事项",结果却给你推荐了一大堆"给狗洗澡"的内…...

西门子博途1500SCL程序和梯形图两者结合编程,包括西门子v90伺服profinet通讯控制

西门子博途1500SCL程序和梯形图两者结合编程,包括西门子v90伺服profinet通讯控制,发那科机器人profinet通讯控制,多profinet io从站,扫码枪串口通讯,触摸屏类似配方功能多行参数显示,模块化结构化编程方式&…...

OpenClaw技能扩展:基于千问3.5-9B的内容处理自动化实践

OpenClaw技能扩展:基于千问3.5-9B的内容处理自动化实践 1. 为什么需要内容处理自动化 作为一个经常需要产出技术文档的开发者,我发现自己每天要重复处理大量内容相关的琐碎工作:从收集资料、整理笔记到生成初稿、调整格式,最后还…...

那些你不知道自己需要监控的 Linux 暗坑期

我为什么会发出这个疑问呢?是因为我研究Web开发中的一个问题时,HTTP请求体在 Filter(过滤器)处被读取了之后,在 Controller(控制层)就读不到值了,使用 RequestBody 的时候。 无论是字…...

【实践】Dify文件下载功能实现与优化指南

1. Dify文件下载功能实现全流程解析 第一次接触Dify文件下载功能时,我也被它独特的存储机制绕晕了。和常见的直接返回文件流的做法不同,Dify的存储类实现更像是"黑箱操作"——文件明明被下载到了指定目录,却找不到返回内容的出口。…...

strlen 和 sizeof 的核心区别

strlen 和 sizeof 的核心区别(超清晰版)这是 C 语言最常考、最易错的知识点,我用最简单的方式给你讲明白:一句话总结sizeof:算内存大小(占多少字节),编译器算,不看内容st…...

智能医学影像分析系统 手骨X光影像的骨折检测与分类任务 手骨x光识别10653期(数据集+模型+界面+代码)

手骨x光识别10653期 README 项目概述 类别 远端指间关节 掌指关节 近端指间关节 桡骨 尺骨 腕部/手腕手骨X光影像数据集分析数据概览关键信息总数量及类别8900,类别:6数据集数量(取整)8900数据格式与应用价值YoloVOC,适…...

JLink 添加国产芯片手把手教程(雅特力 + 华大)

大家好,我是嵌入式学习菌,一名在上海嘉定打拼的嵌入式开发工程师。2023 年 7 月硕士毕业,现深耕嵌入式软件开发,日常和 MCU、调试器打交道。现在项目都在做国产 MCU 替代,雅特力 AT32、华大 HC32 用得越来越多,但 JLink 默认不自带这两家芯片支持,每次都要手动添加。 今…...

AI原生研发ROI断崖预警:2024Q2实测数据揭示——超61%项目在MVP后陷入“伪敏捷成本陷阱”

第一章:AI原生软件研发成本优化实战技巧 2026奇点智能技术大会(https://ml-summit.org) AI原生软件的研发成本常被模型训练开销主导,但实际可观测的浪费更多来自推理服务冗余、提示工程低效、向量数据库未压缩索引及本地开发环境重复构建。聚焦真实生产…...

西安 GEO 服务商有哪些?在到店引流方案中提供哪些关键数据和支持?

在西安,GEO服务商的有效选择直接影响到到店引流方案的实施效果。这些服务商能够提供关键数据支持,比如曝光量、咨询量和转化率,这些数据对于企业评估市场推广效果和优化策略至关重要。企业需要关注服务商的数据透明度,确保其反馈的…...

PDFtoPrinter:在.NET应用中实现高效PDF打印的终极解决方案

PDFtoPrinter:在.NET应用中实现高效PDF打印的终极解决方案 【免费下载链接】PDFtoPrinter .Net Wrapper over PDFtoPrinter util allows to print PDF files. 项目地址: https://gitcode.com/gh_mirrors/pd/PDFtoPrinter 你是否曾经在开发.NET应用时&#x…...

APK-Installer:Windows上的安卓应用安装专家,告别模拟器时代的轻量级解决方案

APK-Installer:Windows上的安卓应用安装专家,告别模拟器时代的轻量级解决方案 【免费下载链接】APK-Installer An Android Application Installer for Windows 项目地址: https://gitcode.com/GitHub_Trending/ap/APK-Installer 在Windows系统中直…...

Multi-Agent 的通信协议:消息格式、上下文共享与信息污染治理

Multi-Agent 的通信协议:消息格式、上下文共享与信息污染治理 1. 引入与连接:从「智能家居鸡同鸭讲」看通信协议的生死线 1.1 核心概念预览 在正式展开前,我们先像看电影预告片一样,抓出这篇文章的三个「核心主角」和一个贯穿始终的「反派危机」: 主角1:Multi-Agent 系…...

太阳能电池缺陷检测数据集:2624张电致发光图像的高性能AI训练基准

太阳能电池缺陷检测数据集:2624张电致发光图像的高性能AI训练基准 【免费下载链接】elpv-dataset A dataset of functional and defective solar cells extracted from EL images of solar modules 项目地址: https://gitcode.com/gh_mirrors/el/elpv-dataset …...

BepInEx插件框架:5分钟掌握Unity游戏模组开发与注入技术

BepInEx插件框架:5分钟掌握Unity游戏模组开发与注入技术 【免费下载链接】BepInEx Unity / XNA game patcher and plugin framework 项目地址: https://gitcode.com/GitHub_Trending/be/BepInEx 如果你热爱Unity游戏并希望为它们添加自定义功能,B…...

告别 AI 失忆!本地部署 MemPalace,原始模式下 96.6% 精准检索

阅读提示:本文基于 MemPalace v0.1(2026-04-06 发布,GitHub: milla-jovovich/mempalace)撰写,项目仍在快速迭代,建议对照官方 README 使用。一、MemPalace 是什么?背景与争议都说清楚 项目来源 …...

沃德绿世界系统小程序开发指南

沃德绿世界系统小程序的开发涉及多个环节,包括需求分析、功能设计、技术实现和上线运营。以下是关键开发步骤:需求分析与规划 明确小程序的定位和目标用户群体,梳理核心功能模块,如会员管理、商品展示、订单处理、积分兑换等。制定…...

MES验收悖论:系统越先进,验收越难——一个食品饮料行业的隐形成本陷阱

大家好,我是东哥说-MES 📚 系列文章目录 🔓 免费试读篇 - [第1篇:免费试读]() ✅ 可立即阅读 🔒 粉丝专享篇(2-n篇需关注后解锁) - [第2篇:进阶应用]() ⭐ 需关注 - [第…...

(学习笔记)3.11 浮点代码(3.11.4 定义和使用浮点数3.11.5 在浮点代码使用位级操作)

文章目录线索栏笔记栏1.定义和使用浮点常数1)核心机制2)示例分析3)练习题3.552.在浮点代码中使用位级操作1)指令与功能2)标量应用3)练习题3.56(逆向工程位操作)总结栏线索栏 为什么…...

倍莱鲜羊奶商城软件源码开发

倍莱鲜羊奶商城软件源码开发要点商城系统架构选择 推荐采用主流电商框架如Shopify、Magento或基于Spring Cloud的微服务架构。后端可选用Java/PHP/Python,前端建议Vue.js/React,数据库MySQL/PostgreSQL。核心功能模块开发 用户模块需实现注册登录、会员积…...