Spring 源码分析之事务2 TransactionStatus与TransactionInfo

Spring 源码分析之事务
Spring 源码分析之事务2 TransactionStatus与TransactionInfo
Spring 源码分析之事务3 事务的提交与回滚

Spring事务TransactionStatus接口相关类解析以及TransactionInfo

首先说一下两个类的作用:
TransactionStatus以及子类:主要描述当前事务的状态,比如:是否有事务,是否是新事物,事务是否只读;回滚点相关操作等等。这些相关的属性在后面会影响事务的提交。
TransactionInfo 主要是持有事务的状态,以及上一个TransactionInfo 的一个引用,并与当前线程进行绑定。主要是为了保证当前请求持有的是自己的事务对象,根据自己的事务状态决定事务的提交与否。

一、TransactionStatus结构图

该类结构图如下:

TransactionStatus.jpg

通过分析我们发现TransactionStatus接口的实现类抽象类AbstractTransactionStatus类主要完成以下功能:

回滚点相关操作

  • 创建回滚点void createAndHoldSavepoint()
  • 设置回滚点void setSavepoint(@Nullable Object savepoint)
  • 获取回滚点 void getSavepoint()
  • 判断是否有回滚点boolean getSavepoint()
  • 释放回滚点void releaseHeldSavepoint()
  • 回滚到回滚点void rollbackToHeldSavepoint()
    注意:在AbstractTransactionStatus中创建回滚点是需要子类去实现getSavepointManager()方法,默认该方法会抛出异常NestedTransactionNotSupportedException("This transaction does not support savepoints")

二、AbstractTransactionStatus的子类DefaultTransactionStatus

image.png

该类也是spring提供的默认实现类。
我们可以看到子类实现了getSavepointManager()如下:

    @Override
    protected SavepointManager getSavepointManager() {
        Object transaction = this.transaction;
        if (!(transaction instanceof SavepointManager)) {
            throw new NestedTransactionNotSupportedException(
                    "Transaction object [" + this.transaction + "] does not support savepoints");
        }
        return (SavepointManager) transaction;
    }

如果想要使用事务的回滚相关操作,继承了AbstractTransactionStatus类的子类必须提供实现,否则在使用事务的回滚点相关操作的时候会抛出异常。

该类还完成了事务相关功能:

  • 获取当前事务Object getTransaction()
  • 是否有激活的事务boolean hasTransaction()
  • 该事务是否是新事务boolean isNewTransaction()
  • 事务是否只读boolean isReadOnly()
  • 返回已为此事务挂起的资源的持有者(如果有)Object getSuspendedResources()

我们可以全局的看看DefaultTransactionStatus结构图:


DefaultTransactionStatus.png

AbstractTransactionStatus 中包含了3条属性:

private boolean rollbackOnly = false;

private boolean completed = false;

@Nullable
private Object savepoint;
  • 是否回滚

  • 是否完成

  • 保存点对象。

所以DefaultTransactionStatus对象最终包含的信息有:

  • 是否回滚

  • 是否完成

  • 保存点对象。

  • 事务对象

  • 是否是新事务的boolean

  • 是否是新的同步boolean

  • 是否只读

  • 挂起的资源对象(Object suspendedResources)

AbstractTransactionStatus 的完成的主要功能:

1.回滚点相关操作(是否有回滚点、设置回滚点、获取回滚点,创建回滚点)
2.标记事务已经完成

三、TransactionInfo

首先protected TransactionInfo createTransactionIfNecessary这个方法会返回一个TransactionInfo ,即在创建事务的时候,会返回。
如下:

protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
            @Nullable TransactionAttribute txAttr, final String joinpointIdentification) {

        ......
        return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);

查看prepareTransactionInfo()方法

protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
            @Nullable TransactionAttribute txAttr, String joinpointIdentification,
            @Nullable TransactionStatus status) {

        TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
        if (txAttr != null) {
            // We need a transaction for this method...
            // The transaction manager will flag an error if an incompatible tx already exists.
            txInfo.newTransactionStatus(status);
        }
        ......

        // We always bind the TransactionInfo to the thread, even if we didn't create
        // a new transaction here. This guarantees that the TransactionInfo stack
        // will be managed correctly even if no transaction was created by this aspect.
        txInfo.bindToThread();
        return txInfo;
    }

此方法完成如下功能:

  • 创建TransactionInfo
  • 将当前的DefaultTransactionStatus,上一步我们分析的包含事务相关状态的对象,设置到TransactionInfo里
  • 将新创建好的TransactionInfo绑定到当前线程(ThreadLocal)
  • 返回TransactionInfo#txInfo.bindToThread()方法,如果存在调用多个业务方法,上一个业务方法的事务信息存哪里了,就存bindToThread()方法里。查看方法:
private void bindToThread() {
            // Expose current TransactionStatus, preserving any existing TransactionStatus
            // for restoration after this transaction is complete.
            this.oldTransactionInfo = transactionInfoHolder.get();
            transactionInfoHolder.set(this);
}

完整的类如下:
TransactionInfo 是抽象类TransactionAspectSupport的一个内部类

这里关注一下
protected static final class TransactionInfo {

   @Nullable
   private final PlatformTransactionManager transactionManager;

   @Nullable
   private final TransactionAttribute transactionAttribute;

   private final String joinpointIdentification;

   @Nullable
   private TransactionStatus transactionStatus;

   @Nullable
   private TransactionInfo oldTransactionInfo;
   
   public void newTransactionStatus(@Nullable TransactionStatus status) {
            this.transactionStatus = status;
        }

        @Nullable
        public TransactionStatus getTransactionStatus() {
            return this.transactionStatus;
        }

        /**
         * Return whether a transaction was created by this aspect,
         * or whether we just have a placeholder to keep ThreadLocal stack integrity.
         */
        public boolean hasTransaction() {
            return (this.transactionStatus != null);
        }

        private void bindToThread() {
            // Expose current TransactionStatus, preserving any existing TransactionStatus
            // for restoration after this transaction is complete.
            this.oldTransactionInfo = transactionInfoHolder.get();
            transactionInfoHolder.set(this);
        }

        private void restoreThreadLocalStatus() {
            // Use stack to restore old transaction TransactionInfo.
            // Will be null if none was set.
            transactionInfoHolder.set(this.oldTransactionInfo);
        }
        .../
}       

如果存在一个业务中法中调用多个其他业务,比如:ServiceA的a方法,调用了ServiceB的b方法和ServiceC的c方法。在调用ServiceB的和调用ServiceC的时候当前的事务对象是什么样子的,是和bindToThread()方法有关的,也就是和属性oldTransactionInfo 和持有transactionInfo的当前线程有关。
这里也可以继续关注一下restoreThreadLocalStatus方法。
没调用一个业务的时候,每个业务方法都有自己的TransactionInfo,每次执行的时候,都会把当前线程中的TransactionInfo取出来,然后新建自己的TransactionInfo,并在自己的TransactionInfo中,将刚刚取出来的TransactionInfo作为oldTransactionInfo 记录到自己的TransactionInfo中。然后每次执行完一个自己的业务逻辑执行,都会再次执行restoreThreadLocalStatus()方法,也就是将本地线程中的TransactionInfo设置为当前TransactionInfo中的oldTransactionInfo。保证每次执行自己的业务的时候,使用的是自己的TransactionInfo即可,保证事务对象不会发生错乱。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,039评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,223评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,916评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,009评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,030评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,011评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,934评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,754评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,202评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,433评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,590评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,321评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,917评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,568评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,738评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,583评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,482评论 2 352