解密Spring事务生效的内部机制
声明式事务和编程式事务对比:
- 声明式事务:
- 使用注解或XML配置的方式,在代码中声明事务的属性和行为。
- 通过AOP和代理模式实现,将事务管理与业务逻辑代码解耦。
- 适用于大多数情况,简化了代码,提高了可维护性和可读性。
- 常用的注解是@Transactional,可以应用在方法或类级别。
- 编程式事务:
- 在代码中显式编写事务管理的代码逻辑。
- 需要手动控制事务的开启、提交和回滚等操作。
- 适用于需要更细粒度控制事务的情况,或者在声明式事务不适用的情况下使用。
- 可以使用编程式事务的API,如Spring的TransactionTemplate或JDBC的事务API。
Spring 事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,Spring 是无法提供事务功能的。对于纯 JDBC 操作数据库,想要用到事务,可以按照以下步骤进行(编程式事务同理)
1、获取连接 Connection con = DriverManager.getConnection()2、开启事务 con.setAutoCommit(true/false);3、执行 CRUD4、提交事务/回滚事务 con.commit() / con.rollback();5、关闭连接 conn.close();

当使用Spring的声明式事务后,我们可以只要写CRUD即可,其他由Spirng 自动完成。
本文主要讲解声明式事务
生效原理
下面是一个简单的示例,用于说明 Spring 事务的生效原理:
@Service
public class MyService {@Transactionalpublic void transferMoney(Account sourceAccount, Account targetAccount, double amount) {//省略逻辑处理}
}
声明式事务就是使用我们常见的@Transactional注解完成的,声明式事务优点就在于让事务代码与业务代码解耦,通过Spring中提供的声明式事务使用,我们也可以发觉我们只需要编写业务代码即可,而事务的管理基本不需要我们操心,Spring就像使用了魔法一样,帮我们自动完成了。
之所以那么神奇,本质还是依靠Spring框架提供的Bean生命周期相关回调接口和AOP结合完成的,简述如下:
这里思考下:如何保证同个线程请求时数据库多个操作使用的是同个Connection?
- 通过自动代理创建器依次尝试为每个放入容器中的bean尝试进行代理
- 尝试进行代理的过程对于事务管理来说,就是利用事务管理涉及到的增强器advisor,即TransactionAttributeSourceAdvisor
- 判断当前增强器是否能够应用与当前bean上,怎么判断呢? —> advisor内部的pointCut喽 !
- 如果能够应用,那么好,为当前bean创建代理对象返回,并且往代理对象内部添加一个TransactionInterceptor拦截器。
- 此时我们再从容器中获取,拿到的就是代理对象了,当我们调用代理对象的方法时,首先要经过代理对象内部拦截器链的处理,处理完后,最终才会调用被代理对象的方法。(这里其实就是责任链模式的应用)
- 当TransactionInterceptor的invoke()方法被执行时,它会先开启一个事务,然后再调用目标方法。如果目标方法执行成功,那么就会提交事务,否则就会回滚事务。
- 在整个事务过程中,
Spring会保证操作数据库时使用的是同一个连接。它是通过将一个数据库连接与一个ThreadLocal对象绑定起来,然后在需要操作数据库时通过该ThreadLocal对象来获取连接的。这样就可以避免多个连接导致的数据不一致问题。

这里说下SpringBoot底层实现有一个AutoProxyRegistrar,简单来说,AutoProxyRegistrar顾名思义就是开启了AOP代理 ProxyTransactionManagementConfiguration是一个配置类,它又定义了另外三个bean:
- BeanFactoryTransactionAttributeSourceAdvisor:一个Advisor就是一个切面
- AnnotationTransactionAttributeSource:相当于Pointcut
- TransactionInterceptor:相当于中的 Advice,事务的拦截方法
AnnotationTransactionAttributeSource就是用来判断某个类上是否存在@Transactional注解, 或者判断某个方法上是否存在@Transactional注解的。
TransactionInterceptor就是代理逻辑,当某个类中存在@Transactional注解时,到时就产生一个 代理对象作为Bean,代理对象在执行某个方法时,最终就会进入到TransactionInterceptor的 invoke()方法。
对于被事务增强器TransactionAttributeSourceAdvisor代理的bean而言,代理对象内部会存在一个TransactionInterceptor,该拦截器内部构造了一个事务执行的模板流程:
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation) throws Throwable {//TransactionAttributeSource内部保存着当前类某个方法对应的TransactionAttribute---事务属性源//可以看做是一个存放TransactionAttribute与method方法映射的池子TransactionAttributeSource tas = getTransactionAttributeSource();//获取当前事务方法对应的TransactionAttributefinal TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);//定位TransactionManagerfinal TransactionManager tm = determineTransactionManager(txAttr);.....//类型转换为局部事务管理器PlatformTransactionManager ptm = asPlatformTransactionManager(tm);final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {//TransactionManager根据TransactionAttribute创建事务后返回//TransactionInfo封装了当前事务的信息--包括TransactionStatusTransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);Object retVal;try {//继续执行过滤器链---过滤链最终会调用目标方法//因此可以理解为这里是调用目标方法retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {//目标方法抛出异常则进行判断是否需要回滚completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {//清除当前事务信息cleanupTransactionInfo(txInfo);}...//正常返回,那么就正常提交事务呗(当然还是需要判断TransactionStatus状态先)commitTransactionAfterReturning(txInfo);return retVal;}...
事务拦截器(TransactionInterceptor)的处理过程总结如下:
-
方法调用前的处理:事务拦截器在目标方法调用前执行一些预处理操作。这包括检查当前是否存在事务上下文(TransactionContext),如果不存在则创建新的事务上下文。事务上下文包含了事务的相关信息,如事务的传播行为、隔离级别、超时设置等。事务拦截器还会根据事务定义的属性决定是否开启新的事务或者加入已有的事务。
-
方法调用:事务拦截器将目标方法调用传递给实际的目标对象,并执行目标方法。
-
方法调用后的处理:在目标方法执行完成后,事务拦截器会根据方法的执行结果决定是提交事务还是回滚事务。如果方法执行过程中发生了异常,事务拦截器会触发事务回滚,将事务恢复到调用该方法之前的状态。如果方法执行成功,事务拦截器会触发事务提交,将事务的操作永久保存到数据库。
事务拦截器通过使用事务管理器(TransactionManager) 来协调和控制事务的开始、提交和回滚操作。事务管理器负责和底层的数据访问框架(如JDBC、Hibernate)进行交互,确保事务的一致性和隔离性。
事务拦截器是通过配置切面(Aspect)和切入点(Pointcut) 的方式与目标方法进行关联。切面定义了在何时何地应用事务拦截器,而切入点则定义了哪些方法会被事务拦截器拦截。通过配置切面和切入点,可以灵活地控制事务的应用范围。
相关文章:
解密Spring事务生效的内部机制
声明式事务和编程式事务对比: 声明式事务: 使用注解或XML配置的方式,在代码中声明事务的属性和行为。通过AOP和代理模式实现,将事务管理与业务逻辑代码解耦。适用于大多数情况,简化了代码,提高了可维护性和…...
大数据时代下的数据安全防护
随着大数据时代的来临,数据安全防护成为了一个重要的问题。在大数据时代,数据的规模和价值都得到了极大的提升,因此数据安全的重要性也变得越来越突出。本文将从数据加密、访问控制、网络安全和人员管理四个方面来介绍大数据时代下的数据安全…...
RabbitMQ-常用命令
RabbitMQ常用命令 3.1 启动停止rabbitMQ命令 # 前台启动Erlang VM 和 RabbitMQ 当窗口关闭或者ctrlc时,使退出了。 rabbitmq-server# 使用系统命令启动 systemctl start rabbitmq-server# 后台启动 rabbitmq-server -detached# 停止rabbitMQ和Erlang VM rabbitmq-…...
Spring中依赖注入的继承bean的细节问题
介绍 有时我们会对一种类型的bean进行继承,在Spring生成bean的时候,返回类型有时是子类类型,有时会父类类型。那么到底在什么情况下用哪种类型呢?肯定有不少人会忽略这点,本篇文章就是把这个细节讲清楚 案例 父类Ba…...
海外腾讯云服务器手机上无法访问外网怎么办??
本文将介绍腾讯云服务器无法访问外网的一些常见原因以及解决办法,同时解答了手机无法访问腾讯云服务器的问题。 腾讯云服务器(Tencent Cloud Server)是一种基于云计算技术的虚拟服务器,可以满足用户对于计算、存储、网络等方面的需…...
python3+requests:接口自动化测试(二)
前言:上篇文章python3requestsunittest:接口自动化测试(一):已经介绍了基于unittest框架的实现接口自动化,但是也存在一些问题,比如最明显的测试数据和业务没有区分开,接口用例不便于…...
uni-app:允许字符间能自动换行(英文字符、数字等)
<template><view class"container"><!-- 这里是你的文本内容 -->{{ multilineText }}</view> </template><style> .container {word-break: break-all; } </style>例如: <template><view class"…...
day 42 |● 121. 买卖股票的最佳时机 ● 122.买卖股票的最佳时机II
121. 买卖股票的最佳时机 dp数组需要记录两种状态,一种是当天时手中还持有股票,一种是当天时手中已卖出股票。 func maxProfit(prices []int) int {dp : make([][]int, len(prices))dp[0] []int{-prices[0], 0}for i : 1; i < len(prices); i{val0…...
SQLserver基础入门理论(超基础)
♥️作者:小刘在C站 ♥️个人主页: 小刘主页 ♥️努力不一定有回报,但一定会有收获加油!一起努力,共赴美好人生! ♥️学习两年总结出的运维经验,以及思科模拟器全套网络实验教程。专栏…...
(三)行为模式:7、观察者模式(Observer Pattern)(C++示例)
目录 1、观察者模式(Observer Pattern)含义 2、观察者模式的UML图学习 3、观察者模式的应用场景 4、观察者模式的优缺点 (1)优点: (2)缺点 5、C实现观察者模式的实例 1、观察者模式&…...
2019CVPR Semantic Graph Convolutional Networks for 3D Human Pose Regression
基于语义图卷积网络的三维人体姿态回归 源码 https://github.com/garyzhao/SemGCN 摘要 在本文中,我们研究了学习图卷积网络(GCN)回归的问题。GCN的当前体系结构受限于卷积滤波器和共享的变换矩阵为的小感受野。为了解决这些限制ÿ…...
大数据课程K16——Spark的梯度下降法
文章作者邮箱:yugongshiyesina.cn 地址:广东惠州 ▲ 本章节目的 ⚪ 了解Spark的梯度下降法; ⚪ 了解Spark的梯度下降法家族(BGD,SGD,MBGD); ⚪ 掌握Spark的MLlib实现…...
springboot:时间格式化的5种方法(解决后端传给前端的时间格式转换问题)推荐使用第4和第5种!
本文转载自:springboot:时间格式化的5种方法(解决后端传给前端的时间显示不一致)_为什么前端格式化日期了后端还要格式化_洛泞的博客-CSDN博客 时间问题演示 为了方便演示,我写了一个简单 Spring Boot 项目ÿ…...
六、vim编辑器的使用
1、编辑器 (1)编辑器就是一款软件。 (2)作用就是用来编辑文件,譬如编辑文字、编写代码。 (3)Windows中常用的编辑器,有自带的有记事本(notepad),比较好用的notepad、VSCode等。 (4)Linux中常用的编辑器,自带的最古老的vi&…...
【易售小程序项目】项目介绍与系列文章集合
项目介绍 易售二手小程序主要用于校园中二手商品的交易,该系列文章会记录这个小程序前端的整个开发过程并提供详细代码,后台主要基于若依管理系统搭建,文章中也会提及后端关键部分的实现及代码。希望该系列文章可以帮助小白了解项目的开发流…...
游戏服务器成DDoS最大攻击重灾区
游戏产业的迅猛发展也让游戏产业成为被黑客攻击的重灾区。什么原因让游戏行业成为DDoS的攻击重点。总结有如下原因和主要手段: 1.游戏行业的攻击成本较低,攻防成本1:N。随着DDoS攻击的打法越来越复杂,攻击点更是越来越多ÿ…...
[SpringBoot3]博客管理系统(源码放评论区了)
八、博客管理系统 创建新的SpringBoot项目,综合运用以上知识点,做一个文章管理的后台应用。依赖: Spring WebLombokThymeleafMyBatis FrameworkMySQL DriverBean Validationhutool 需求:文章管理工作,发布新文章&…...
C语言——指针基本语法
概述 内存地址 在计算机内存中,每个存储单元都有一个唯一的地址(内存编号)。 通俗理解,内存就是房间,地址就是门牌号 指针和指针变量 指针(Pointer)是一种特殊的变量类型,它用于存储内存地址。 指针的实…...
elementui table 在浏览器分辨率变化的时候界面异常
异常点: 界面显示不完整,表格卡顿,界面已经刷新完成,但是表格的宽度还在一点一点变化,甚至有无线延伸的情况 思路: 1. 使用doLayout 这里官方文档有说明, 所以我的想法是,监听浏览…...
六、Kafka-Eagle监控
目录 6.1 MySQL 环境准备6.2 Kafka 环境准备6.3 Kafka-Eagle 安装 6.1 MySQL 环境准备 Kafka-Eagle 的安装依赖于 MySQL,MySQL 主要用来存储可视化展示的数据 6.2 Kafka 环境准备 修改/opt/module/kafka/bin/kafka-server-start.sh 命令 vim bin/kafka-server-sta…...
3个步骤快速定位Windows热键占用者:Hotkey Detective完整实战指南
3个步骤快速定位Windows热键占用者:Hotkey Detective完整实战指南 【免费下载链接】hotkey-detective A small program for investigating stolen key combinations under Windows 7 and later. 项目地址: https://gitcode.com/gh_mirrors/ho/hotkey-detective …...
用Matlab给变形镜建模:从高斯函数到贝塞尔曲线,两种响应函数仿真全流程
用Matlab给变形镜建模:从高斯函数到贝塞尔曲线,两种响应函数仿真全流程 光学系统工程师在设计自适应光学系统时,经常需要精确模拟变形镜的响应特性。这种模拟不仅关系到系统性能预测的准确性,也直接影响控制算法的开发效率。本文将…...
CANN/asc-devkit原子减法操作
asc_atomic_sub 【免费下载链接】asc-devkit 本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。 项目地址: https://gitcode…...
LangGraph 是什么?为什么它越来越像 AI Agent 时代的“操作系统”
文章目录一、为什么普通的“聊天式 AI”不够用了?1. 状态容易丢2. 流程难控制3. 执行失败后很难恢复4. 决策过程不透明二、LangGraph 到底是什么?1. 编排2. 运行时三、为什么很多人会说:LangGraph 像 Agent Server 的“操作系统”?…...
基于 Transformer 架构的翻译模型实践 - 主流分词器(Tokenizer)的对比
基于 Transformer 架构的翻译模型实践 - 主流分词器(Tokenizer)的对比 flyfish 参考 https://github.com/shaoshengsong/ pytorch -transformer-en-zh-translation-demo对hello不同的分词方案可以分为单个字符【h,e,l,…...
14.3 异步协程开发铁律示例 与 标准示例代码核心:事件循环内严禁编写同步逻辑,协程业务务必全程异步
Python异步协程从原理到实战完整总结 一、协程底层核心 asyncio 基于单线程事件循环驱动运行,通过 await 主动让出执行权完成任务切换,切换开销远低于多线程,天生适配IO密集型业务场景; 单线程特性决定它无法直接利用多核处理CPU密…...
全息三维空间孪生,全域无感精准智位系列:UWB:多路径干扰精度失稳|镜像:多源时空误差融合
在全域空间数字化、实景虚实融合与空间智能快速演进的产业周期中,镜像视界(浙江)科技有限公司持续深耕视频原生三维重构、时空AI像素解算、全域无感精准定位、跨镜轨迹智能推演底层核心领域,依托八大自主可控核心引擎构筑全栈技术…...
OpenAnolis峰会技术干货:从内核优化到云原生实战与开源参与
1. 项目概述:一场不容错过的技术盛宴如果你是一名长期耕耘在操作系统、云计算或基础软件领域的开发者或技术决策者,那么“2022全球开源峰会OpenAnolis分论坛”这个标题,对你而言绝不仅仅是一场普通的线上或线下会议通知。它更像是一份来自技术…...
OpenMMLab环境配置避坑指南:从CUDA 11.6到PyTorch 1.13,如何为MMRotate 0.3.4找到对的mmcv-full?
OpenMMLab精准环境配置实战:破解CUDA 11.6与PyTorch 1.13下的mmcv-full匹配困局 当你在RTX 3060显卡上尝试运行MMRotate 0.3.4时,突然发现控制台抛出ImportError: cannot import name get_dist_info from mmcv.runner——这往往是深度学习工程师与OpenMM…...
【计算机组成原理】无符号整数乘法原理(基于移位累加,零基础看懂CPU乘法)
前言在数字电路与计算机组成原理中,加法是最基础的运算,而乘法是高频常用运算。很多初学者疑惑:计算机没有专门的乘法口诀,到底怎么实现二进制乘法?而在数字运算中,乘法是比加法更复杂、但底层逻辑完全依托…...
