解密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…...
大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
Unity UGUI Button事件流程
场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...
c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
Java并发编程实战 Day 11:并发设计模式
【Java并发编程实战 Day 11】并发设计模式 开篇 这是"Java并发编程实战"系列的第11天,今天我们聚焦于并发设计模式。并发设计模式是解决多线程环境下常见问题的经典解决方案,它们不仅提供了优雅的设计思路,还能显著提升系统的性能…...
