【Spring框架】Spring事务同步

news2024/12/25 2:01:29

目录

一、什么是Spring事务同步

二、 事务同步管理器

2.1 TransactionSynchronizationManager事务同步管理器

2.1.1 资源同步

2.1.2 事务同步

2.1.3 总结

三、事务同步管理器保障事务的原理

四、spring事务为何使用TransactionSynchronizationManager

spring源码实现

五、TransactionSynchronization--(before/after-commit/comple)


一、什么是Spring事务同步

Spring 将 JDBC 的 Connection、Hibernate 的 Session 等访问数据库的连接或者会话对象统称为资源,这些资源在同一时刻是不能多线程共享的 。 为了让 DAO 或 Service 类可以实现单例模式,Spring的事务同步管理器org.springframework.transaction.support.TransactionSynchronizationManager类利用 ThreadLocal 为不同的事务线程提供了独立的资源副本,并同时维护这些事务的配置属性和运行状态信息 。它提供了一些静态方法来操作和获取线程绑定的资源,如bindResource、getResource、unbindResource等。

Spring的事务同步还涉及到事务传播行为,即在一个事务方法中调用另一个事务方法时,后者的事务如何与前者的事务关联。

总的来说Spring事务同步的作用就是在不同的事务线程中保证资源的一致性和事务的正确性。Spring事务同步的主要功能有:

  • 通过ThreadLocal为每个事务线程提供独立的资源副本,如数据库连接、会话对象等,避免线程安全问题。
  • 通过TransactionSynchronizationManager类管理和操作线程绑定的资源,如绑定、获取、解绑等。
  • 通过事务传播行为控制不同事务方法之间的事务关联,如是否使用同一个事务、是否创建新的事务等。
  • 通过事务同步器(TransactionSynchronization)实现事务的扩展功能,如在事务提交前后执行一些额外的操作。

二、 事务同步管理器

2.1 TransactionSynchronizationManager事务同步管理器

TransactionSynchronizationManager事务同步管理器,管理每个线程的资源(对于事务,DataSource创建的连接对象connection等称作事务的资源)和事务同步(TransactionSynchronization---用来监听事务操作的回调类,其中定义了在事务执行过程中,进行的拓展操作,如before/after--commit/completion,在getSynchronizations中,对其进行sort排序返回)。

同步分两种,资源的同步和事务的同步。

2.1.1 资源同步

此处就是数据库DataSource的连接connection,保证在一个线程中的事务操作,能够获取同一个connection资源。因此资源存入ThreadLocal<Map<Object, Object>> resources中,保证线程之间的事务操作的隔离(因为获取不同connection,由数据库事务实现spring事务)

2.1.2 事务同步

也就是事务方法同步,synchronizations内的TransactionSynchronization对象的集合,其用来在事务提交前、后,事务完成前后进行的实际操作,其事务操作在各个阶段的执行流程在AbstractPlatformTransactionManager中定义。

2.1.3 总结

TransactionSynchronizationManager 通过 ThreadLocal 对象在当前线程记录了 resources 和 synchronizations属性。resources 是一个 HashMap,用于记录当前参与事务的事务资源,方便进行事务同步在 DataSourceTransactionManager 的例子中就是以 dataSource 作为 key,保存了数据库连接,这样在同一个线程中,不同的方法调用就可以通过 dataSource 获取相同的数据库连接,从而保证所有操作在一个事务中进行。synchronizations 属性是一个 TransactionSynchronization对象的集合,AbstractPlatformTransactionManager 类中定义了事务操作各个阶段的调用流程

三、事务同步管理器保障事务的原理

spring的事务通过数据库DataSource获取connection来实现,为了使事务方法service.methodA,调用dao.methodB时,仍然能够位于当前事务,如此,能够使得service和dao的调用,在同线程的情况下,都可以获取到相同的connection,就保证了两个操作都在同一个事务。所以需要将connection共享,并考虑使用线程共享变量,threadLocal<Map(datasource,connection)>保存共享的connection。

public abstract class TransactionSynchronizationManager {
    /**…………………………………………………………………………………………………………同步资源……………………………………………………………………………………………… */
    // resource相当于一个(threadID,map(datasource,connectionHolder))的属性,这里用ThreadLocal保存
    // key为DataSource,value为connectionHolder(保存当前threadID的connection)
    // 也就是存储的当前线程ID中不同的数据源DataSource对应的connection,这样能保证在同一个事务线程中,获取相同数据源DataSource的connection都是同一个connection
    private static final ThreadLocal<Map<Object, Object>> resources =
            new NamedThreadLocal<>("Transactional resources");
    
    /**………………………………………………………………………TransactionSynchronization管理…………………………………………………………………………………………… */
    // 当前线程所需要的事务同步器TransactionSynchronization集合
    // TransactionSynchronization用来在事务的执行阶段前后,进行回调操作,如before/after-commit/completion
    private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
            new NamedThreadLocal<>("Transaction synchronizations");
    
    // 事务同步管理器一起维护这些事务属性,保证事务属性的一致性和正确性
    // 当前事务名称
    private static final ThreadLocal<String> currentTransactionName =
            new NamedThreadLocal<>("Current transaction name");
    private static final ThreadLocal<Boolean> currentTransactionReadOnly =
            new NamedThreadLocal<>("Current transaction read-only status");
    // 当前事务的隔离级别
    private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
            new NamedThreadLocal<>("Current transaction isolation level");
    // 当前事务是否active
    private static final ThreadLocal<Boolean> actualTransactionActive =
            new NamedThreadLocal<>("Actual transaction active");
    
    // 获取当前是否存在事务(判断线程共享变量,是否存在TransactionSynchronization
    public static boolean isSynchronizationActive() {
        return (synchronizations.get() != null);
    }
       
    /**……………………………………………………………………获取connection资源……………………………………………………………………………… */
    @Nullable
    public static Object getResource(Object key) {
        Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
        return doGetResource(actualKey);
    }
    @Nullable
    private static Object doGetResource(Object actualKey) {
        Map<Object, Object> map = resources.get();
        if (map == null) {
            return null;
        }
        Object value = map.get(actualKey);
        // Transparently remove ResourceHolder that was marked as void(无效的)...
        if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
            map.remove(actualKey);
            // Remove entire ThreadLocal if empty...
            if (map.isEmpty()) {
                resources.remove();
            }
            value = null;
        }
        return value;
    }
    
    //DataSourceTransactionManager.doBegin方法中,将新创建的connection包装成connectionHolder,并存入ThreadLocal<Map<Object, Object>> resources
    public static void bindResource(Object key, Object value) throws IllegalStateException {
        Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
        Assert.notNull(value, "Value must not be null");
        Map<Object, Object> map = resources.get();
        // set ThreadLocal Map if none found
        if (map == null) {
            map = new HashMap<>();
            resources.set(map);
        }
        Object oldValue = map.put(actualKey, value);
        // Transparently suppress a ResourceHolder that was marked as void...
        if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {
            oldValue = null;
        }
        if (oldValue != null) {
            throw new IllegalStateException(
                    "Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread");
        }
    }
    
  /**……………………………………………………………………获取TransactionSynchronization资源……………………………………………………………………………… */
    public static List<TransactionSynchronization> getSynchronizations() throws IllegalStateException {
        Set<TransactionSynchronization> synchs = synchronizations.get();
        if (synchs == null) {
            throw new IllegalStateException("Transaction synchronization is not active");
        }
        // Return unmodifiable snapshot, to avoid ConcurrentModificationExceptions
        // while iterating and invoking synchronization callbacks that in turn
        // might register further synchronizations.
        if (synchs.isEmpty()) {
            return Collections.emptyList();
        }
        else {
            // Sort lazily here, not in registerSynchronization.
            List<TransactionSynchronization> sortedSynchs = new ArrayList<>(synchs);
            //TransactionSynchronization进行排序
            OrderComparator.sort(sortedSynchs);
            return Collections.unmodifiableList(sortedSynchs);
        }
    }
}

四、spring事务为何使用TransactionSynchronizationManager

一个web项目的主要逻辑模块如下:

spring的事务,通常是在service层的某个方法做完整的事务。service要完成事务,需要如下点:

  1. service在系统中是个单例对象,且service需要通过持有DataSource.connection连接对象,通过数据库事务来实现spring事务;
  2. 一个DataSource可以创建多个connection,每个conn需要被一个线程在执行service时候持有,才能在当前调用中完整的通过获取同一个conn实现事务的begin、commit、rollback;
  3. service调用,需要与DAO层交互,因此也要保证DAO也可以获取同一个conn;
  4. Datasource应该单独放在一个类中,以便对于不同的用户线程执行service事务时DataSource.getConnection获取连接。

因此,考虑如上几点需求,需要将不同线程对应的conn存放在ThreadLocal中,能够保证事务执行中,同个线程都可以跨模块获取该conn,保证处于同一事务中。

spring中通过事务同步管理器TransactionSynchronizationManager实现上述推论。

public abstract class TransactionSynchronizationManager {
    /**…………………………………………………………………………………………………………同步资源……………………………………………………………………………………………… */
    //resource相当于一个(threadID,map(datasource,connectionHolder))的属性,这里用ThreadLocal保存
    //key为DataSource,value为connectionHolder(保存当前threadID的connection)
    private static final ThreadLocal<Map<Object, Object>> resources =
            new NamedThreadLocal<>("Transactional resources");
    
    ...
}

spring事务中DataSource,通过DataSourceTransactionManager保存。

spring源码实现

事务开启,通过transactionManager.getTransaction。

// AbstractPlatformTransactionManager.java
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
        throws TransactionException {
    TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
    // 获取事务对象DataSourceTransactionObject
    Object transaction = doGetTransaction();
    ...
}
// DataSourceTransactionManager.java
protected Object doGetTransaction() {
    DataSourceTransactionObject txObject = new DataSourceTransactionObject();
    txObject.setSavepointAllowed(isNestedTransactionAllowed());
    ConnectionHolder conHolder =
        //TransactionSynchronizationManager通过DataSource创建connection,包装成ConnectionHolder
        (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
    //设置conHolder入事务对象DataSourceTransactionObject
    txObject.setConnectionHolder(conHolder, false);
    return txObject;
}
 
public abstract class TransactionSynchronizationManager {
    private static final ThreadLocal<Map<Object, Object>> resources =
            new NamedThreadLocal<>("Transactional resources");
 
    public static Object getResource(Object key) {
        Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
        Object value = doGetResource(actualKey);
        return value;
    }
 
    private static Object doGetResource(Object actualKey) {
        //从ThreadLocal<Map<Object, Object>> resources中获取当前线程对应的事务连接connection
        Map<Object, Object> map = resources.get();
        if (map == null) {
            return null;
        }
        Object value = map.get(actualKey);
        return value;
    }
}

五、TransactionSynchronization--(before/after-commit/comple)

事务同步器,我们可以自定义实现TransactionSynchronization类,来监听Spring的事务操作,通过TransactionSynchronization类的这些回调方法做一些扩展。

public interface TransactionSynchronization extends Flushable {
    //描述事务当前状态
    int STATUS_COMMITTED = 0;
    int STATUS_ROLLED_BACK = 1;
    int STATUS_UNKNOWN = 2;
    //挂起该事务同步器
    default void suspend() {}
    //恢复事务同步器
    default void resume() {}
    //flush 底层的session到数据库
    @Override
    default void flush() {}
    //事务提交前的回调
    default void beforeCommit(boolean readOnly) {}
    //事务commit/rollback前的回调,用于在事务完成前进行资源清除
    default void beforeCompletion() {}
    //事务提交后的回调,可以用于在事务成功提交后做的进一步的操作,例如:在数据提交到数据库中后,发送确认的短信或邮件
    default void afterCommit() {}
    //在事务commit/rollback之后进行回调,例如可以在事务完成后做一些资源清除
    default void afterCompletion(int status) {}
}

举例:

TransactionSynchronization是注册在TransactionSynchronizationManager内,需要其内的方法判断是否存在事务,是否可以执行事务同步方法。

// 当前事务提交后方可进行异步任务,防止异步任务先于未提交的事务执行
private void callBack(Invoice invoice){
    boolean synchronizationActive = TransactionSynchronizationManager.isSynchronizationActive();
    // 当前存在事务,在事务提交后执行  
    if (synchronizationActive) { 
        TransactionSynchronizationManager.registerSynchronization(
            new TransactionSynchronizationAdapter() {
                @Override
                public void afterCommit() { // 监听事务提交完成
                    // 事务提交后执行异步任务
                    doCall(invoice);
                }
            }
        );
    } else {
        // 当前不存在事务,直接执行
        doCall(invoice);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1444130.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

哪家洗地机比较好用?性能好的洗地机推荐

在众多功能中&#xff0c;我坚信洗地机的核心依旧是卓越的清洁能力以及易于维护的便捷性&#xff0c;其他的附加功能可以看作是锦上添花&#xff0c;那么如何找到性能好的洗地机呢&#xff1f;我们一起看看哪些洗地机既能确保卫生效果还能使用便利。 洗地机工作原理&#xff1…

【leetcode热题100】子集 II

给你一个整数数组 nums &#xff0c;其中可能包含重复元素&#xff0c;请你返回该数组所有可能的子集&#xff08;幂集&#xff09;。 解集 不能 包含重复的子集。返回的解集中&#xff0c;子集可以按 任意顺序 排列。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,2] 输出…

基于javaEE的ssm仓库管理系统

仓库管理系统的重中之重是进销存分析这一板块&#xff0c;在这一板块中&#xff0c;顾名思义能够查询到近期的进货记录&#xff0c;包括每日的进货单据&#xff0c;单品推移(即某一商品的库存变化)&#xff0c;方便我们核对库存差异。同时也需要查询到每日的销售数据&#xff0…

Linux服务器安装Jenkins

1、安装Jenkins前必须先安装jdk与maven 2、下载Jenkins 安装包地址 linux jenkins 链接: 百度网盘 请输入提取码 提取码: zfyq 3、解压压缩包 rpm -ivh jenkins-2.174-1.1.noarch.rpm 4、解压完成后查看Jenkins安装路径 whereis jenkins 5、启动报错 &#xff0c;这是因为Jenki…

vs2013与对应的opencv3.2下载地址和环境配置

Visual Studio community 2013 • 链接&#xff1a; https://pan.baidu.com/s/1ye-EDDyUGsy8EaZKaq9Ksw 提取码&#xff1a; 06lw -- 来自百度网盘超级会员 V7 的分享 • 也可以从下面官网下载 https://learn.microsoft.com/zh-tw/visualstudio/releasenotes/vs2013-communit…

前端JavaScript篇之对象创建的方式有哪些?

目录 对象创建的方式有哪些&#xff1f;1. 工厂模式&#xff1a;2. 构造函数模式&#xff1a;3. 原型模式&#xff1a;4. 混合模式&#xff1a;5. 动态原型模式&#xff1a;6. 寄生构造函数模式&#xff1a;7. 字面量方式&#xff1a; 对象创建的方式有哪些&#xff1f; JavaS…

RUST入门:如何用vscode调试rust程序

RUST已经流行一阵子了&#xff0c;但是比较系统的IDE介绍还是比较少&#xff0c;这里我简单介绍 一下如何用vscode实现单步调试rust程序&#xff0c;就像我们平时调试c程序一样。 学习资料网站 首先&#xff0c;介绍几个学习rust的好网站&#xff0c; Rust程序设计语言Rust语…

vue.js基于springboot的实验室设备管理系统10345

(1)设备信息模块&#xff1a;记录设备的基本信息&#xff0c;如设备采购来源信息、设备需求量、当前数量、日期等。 (2) 用户模块&#xff1a;教师职工。实现对用户个人信息、消息管理和实验室设备的查询使用申请等。 (3) 管理员模块&#xff1a;实现对所有设备信息的增删改查&…

论文笔记:相似感知的多模态假新闻检测

整理了RecSys2020 Progressive Layered Extraction : A Novel Multi-Task Learning Model for Personalized Recommendations&#xff09;论文的阅读笔记 背景模型实验 论文地址&#xff1a;SAFE 背景 在此之前&#xff0c;对利用新闻文章中文本信息和视觉信息之间的关系(相似…

迅为RK3588开发板ubuntu和window互传图形界面直接拖拽进行文件传输

确保以及安装了 VMware Tools。如下图所示表示已安装过了。 和 windows 端文件夹间传输一样直接拖拽进去即可&#xff0c;如下图所示&#xff1a; 也可拖拽到终端&#xff0c;如下图所示&#xff1a; 更多内容可以B站搜索迅为RK3588开发板

不安全的 HTTP请求 漏洞原理以及修复方法

漏洞名称&#xff1a;不安全的HTTP方法、危险的HTTP方法 漏洞描述&#xff1a;不安全的HTTP方法一般包括&#xff1a;TRACE、PUT、DELETE、COPY 等。其中最常见的为TRACE方法可以回显服务器收到的请求&#xff0c;主要用于测试或诊断&#xff0c;恶意攻击者可以利用该方法进行…

【Linux】学习-基础IO—下

Linux基础IO—上 重定向 通过上篇的学习&#xff0c;我们了解了文件描述符的分配规则是遍历指针数组&#xff0c;用没有被使用的最小下标作为新的文件描述符&#xff0c;也就是我们可以通过关闭三个标准流文件并使用他们原先所占用的0&#xff0c;1&#xff0c;2描述符。 那…

2024 年 6 款最佳 PDF 编辑器,您可以免费获得

PDF 作为与 Windows、iOS、Linux 和各种其他操作系统兼容的安全文档格式而享有盛誉。这种广泛的兼容性使 PDF 成为一种流行的选择&#xff0c;几乎每个用户都会在不同的环境中遇到 PDF 文件。无论是合同、发票、电子书、信用卡对账单、银行对账单、税务表格还是保险文件&#x…

【芯片设计- RTL 数字逻辑设计入门 番外篇 9 -- SOC 中PL端与PS端详细介绍】

文章目录 Programmable Logic and Processing SystemPL&#xff08;Programmable Logic&#xff09;特点PS和PL之间的协同设计和开发工具 Programmable Logic and Processing System 在系统级芯片&#xff08;SoC&#xff09;的上下文中&#xff0c;“PL” 通常指的是可编程逻…

第二节 zookeeper基础应用与实战

目录 1. Zookeeper命令操作 1.1 Zookeeper 数据模型 1.2 Zookeeper服务端常用命令 1.3 Zookeeper客户端常用命令 1.3.1 基本CRUD 1.3.2 创建临时&顺序节点 2. Zookeeper JavaAPI操作 2.1 Curator介绍 2.2 引入Curator 2.3 建立连接 2.4 添加节点 2.5 修改节点 …

Blazor SSR/WASM IDS/OIDC 单点登录授权实例3-服务端管理组件

目录: OpenID 与 OAuth2 基础知识Blazor wasm Google 登录Blazor wasm Gitee 码云登录Blazor SSR/WASM IDS/OIDC 单点登录授权实例1-建立和配置IDS身份验证服务Blazor SSR/WASM IDS/OIDC 单点登录授权实例2-登录信息组件wasmBlazor SSR/WASM IDS/OIDC 单点登录授权实例3-服务端…

导数的定义【高数笔记】

【含义】可以抽象成&#xff0c;在一个极其短的时间段内&#xff0c;温度差 / 时间差 【本质】瞬间的平均值 【分类】可以分成几类&#xff1f;每类需要注意的点 【导数存在的必要条件】 【导数与极限的关系】可以参考导数的定义的式子 【题型解法】分几个题型&#xff1f;每个…

【MySQL进阶之路】生产案例:每一个月左右MySQL就会出现性能抖动问题

欢迎关注公众号&#xff08;通过文章导读关注&#xff1a;【11来了】&#xff09;&#xff0c;及时收到 AI 前沿项目工具及新技术的推送&#xff01; 在我后台回复 「资料」 可领取编程高频电子书&#xff01; 在我后台回复「面试」可领取硬核面试笔记&#xff01; 文章导读地址…

Linux第45步_通过搭建“DNS服务器”学习图形化配置工具

学习的意义&#xff1a;通过搭建“DNS服务器”&#xff0c;来学习“图形化配置工具”。“DNS服务器”&#xff0c;我们用不到&#xff0c;但为后期移植linux系统服务&#xff0c;因为在移植系统时&#xff0c;需要用到这个“图形化配置工具”。 1、“menuconfig图形化配置工具…

贾玲的腹肌,你也可以拥有

​​​​​​​ 贾玲的腹肌&#xff0c;你也可以拥有 大年初一&#xff0c;有学员来给顾问老师拜年&#xff0c;聊起了现在春节档热门电影&#xff0c;贾玲导演的第二部作品《热辣滚烫》&#xff0c;也聊起了她瘦身100斤后的模样。 学员&#xff1a;贾玲瘦了 100 斤&#xff0…