【Spring事物三千问】TransactionSynchronizationManager的原理分析

TransactionSynchronizationManager

TransactionSynchronizationManager 是管理每个线程的DB连接资源和事务同步的核心委托类。

如果事务同步未处于活动状态,则表示当前没有事务,或者事务管理器不支持事务同步。

TransactionSynchronizationManager 中定义了很多 ThreadLocal 变量,来保存事务相关的信息

public abstract class TransactionSynchronizationManager {

    // 保存DB连接资源。
    // 使用 Object 来保存是因为每种平台的DB连接资源对象可能不一样,比如:JDBC,Hibernate,EJB 等使用的 DB 连接对象是不一样的。 
    private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources");

    // 事务同步回调。每个线程可以注册多个事务同步回调
    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");
    // 事务是否开启  
    private static final ThreadLocal<Boolean> actualTransactionActive = new NamedThreadLocal<>("Actual transaction active");
}

不同的平台的DB连接资源对象可能是不一样的,反映在 Spring 源码当中就是调用 TransactionSynchronizationManager#bindResource() 方法时,绑定的 DB 连接资源不同。

TransactionSynchronizationManager 事务同步管理器的核心逻辑

  • 为什么要使用事务同步管理器 TransactionSynchronizationManager?
    答:不同平台的事务管理实现方式不同,DB 连接资源对象也可能不同,所以,通过 TransactionSynchronizationManager 将事务资源相关的信息保存在各个 ThreadLocal 对象中,可以隔离不同平台带来的差异。

将当前 DB 连接资源绑定到当前线程中的代码如下:
doGetConnection.png

不管是什么平台,Spring 封装的获取 DB 连接资源的编程模型都是统一的:

  1. 从当前线程中获取绑定的 DB 连接资源 — TransactionSynchronizationManager#getResource()
  2. 如果没有获取到,则从 DataSource 中获取 DB 连接资源
  3. 将获取到的 DB 连接资源绑定到当前线程中 — TransactionSynchronizationManager#bindResource()

TransactionSynchronization 事务回调扩展

Spring 会通过 AbstractPlatformTransactionManager#processCommit() 去处理事务的提交,在事务提交前后预留了一些扩展点。

处理事务提交的代码如下:
processCommit.png

可以看到,事务提交前后有一些 triggerXxx 方法是 Spring 预留的扩展点。底层是调用当前线程中注册的事务同步回调 TransactionSynchronization 中的方法。

// TransactionSynchronizationUtils#triggerBeforeCommit()  
public static void triggerBeforeCommit(boolean readOnly) {
    for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {
        synchronization.beforeCommit(readOnly);
    }
}

事务回调的扩展方法有: triggerBeforeCommit()、triggerBeforeCompletion()、triggerAfterCommit()、triggerAfterCompletion()
参考: org.springframework.transaction.support.TransactionSynchronization

https://www.cnblogs.com/kkkfff/p/13778692.html
https://carlzone.blog.csdn.net/article/details/108659198

慎用事务回调扩展

问题一:triggerXx 方法中操作数据库的话,对原事物是否有影响?

TransactionSynchronization 扩展出的方法中,能不能在方法里面操作数据库,要打个问号??
这里面带来的问题是:如果在 triggerXx 方法中操作数据库,是新开启一个连接,还是在原来的事务连接中进行操作?
如果操作不当,容易引发问题,要慎用! https://www.jianshu.com/p/149de22ca54b

其实,这些预留扩展的 trigger 方法都可以通过其他变通的方式来书写,没必要一定要使用。在博主的工作中,还从未碰到过不使用 trigger 扩展解决不了的场景。

问题二:triggerAfterCommit()、triggerAfterCompletion()

在阅读源码的过程中,可以发现:
在 triggerAfterCommit()、triggerAfterCompletion() 中使用事物的话,不会对上层事物产生任何影响了,因为 spring-tx 对异常的处理没有包含 triggerAfterCommit()、triggerAfterCompletion(),
所以,上层事物不会再处理 triggerAfterCommit()、triggerAfterCompletion() 方法里面抛出的异常。

还有个问题是需要尤其注意的,triggerAfterCommit()、triggerAfterCompletion() 方法中使用事物的话,还有一个风险,内外事物如果使用的是同一个事物的话,那么,事物连接在这个时候可能已经被
连接池回收了,从而导致诡异的问题。

可以参看源码注释: org.springframework.transaction.support.TransactionSynchronization#afterCompletion
TransactionSynchronization#afterCommit 与 TransactionSynchronization#afterCompletion 是类似的。

Invoked after transaction commit/rollback. Can perform resource cleanup after transaction completion.
NOTE: The transaction will have been committed or rolled back already, but the transactional resources might still be active and accessible.
As a consequence, any data access code triggered at this point will still "participate" in the original transaction, 
allowing to perform some cleanup (with no commit following anymore!), unless it explicitly declares that it needs to run in a separate transaction. 
Hence: Use PROPAGATION_REQUIRES_NEW for any transactional operation that is called from here.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老王学源码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值