Spring Dao编程原理-声明式事务的控制原理

从 0 开始深入学习 Spring 专栏目录总览

上一章,咱把声明式事务的两种定义方式,其底层的生效原理都搞明白了,接下来本章要研究的,是在实际的代码中,声明式事务如何发挥其控制效果的。

咱以 com.linkedbear.spring.transaction.d_declarativeanno.DeclarativeTransactionAnnoApplication 中的测试代码作为测试用例,来研究控制原理。

0. 程序运行

在测试代码中,咱把断点打在 accountService.transfer(1, 2, 100); 上:

public class DeclarativeTransactionAnnoApplication {
    
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(DeclarativeTransactionConfiguration.class);
//        UserService userService = ctx.getBean(UserService.class);
//        userService.saveAndQuery();
    
        AccountService accountService = ctx.getBean(AccountService.class);
        accountService.transfer(1, 2, 100);
    }
}

之后以 Debug 的形式运行,等断点停在此处时,Debug 进入。。。

1. CglibAopProxy#intercept

想都不用想,肯定先进 AOP 的代理拦截方法啦,借助 IDEA 可以发现这里面确实有一个 Advisor 增强器,而且就是上一章看到的 BeanFactoryTransactionAttributeSourceAdvisor :

Spring Dao编程原理-声明式事务的控制原理

那接下来的代码调试一下子就有目标了,咱直接来到 BeanFactoryTransactionAttributeSourceAdvisor 内部组合的 TransactionInterceptor 中:

2. TransactionInterceptor#invokeWithinTransaction

来到 TransactionInterceptor 的 invoke 方法,可以发现它直接在下面调用了一个 invokeWithinTransaction 方法:

@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
    Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
    return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}

进入 invokeWithinTransaction 方法,发现这个方法是在父类 TransactionAspectSupport 中定义的。

由于这个方法篇幅很长,为了让小伙伴更好的阅读源码和理解源码,小册依然将其拆分成多个片段讲解。

2.1 获取TransactionAttribute和TransactionManager

@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
        final InvocationCallback invocation) throws Throwable {

    // If the transaction attribute is null, the method is non-transactional.
    TransactionAttributeSource tas = getTransactionAttributeSource();
    final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
    final TransactionManager tm = determineTransactionManager(txAttr);
    // ......

首先一上来,它会先拿到 TransactionAttributeSource ,然后根据当前执行的方法和目标类,取出对应的 TransactionAttribute ,再取出事务管理器。整个动作一气呵成,不过这里面可有学问。

2.1.1 【解析注解】获取事务定义信息

tas.getTransactionAttribute(method, targetClass) 获取事务定义信息,即 TransactionAttribute 的时候,这里面涉及到一个缓存机制,咱 Debug 跟进 AbstractFallbackTransactionAttributeSource 类中看看:(重要部分注释已标注在源码中)

private final Map<Object, TransactionAttribute> attributeCache = new ConcurrentHashMap<>(1024);

public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    if (method.getDeclaringClass() == Object.class) {
        return null;
    }

    // First, see if we have a cached value.
    // 根据method和targetClass,构造一个缓存键
    Object cacheKey = getCacheKey(method, targetClass);
    // 如果能从缓存中取出事务定义信息,则直接返回
    TransactionAttribute cached = this.attributeCache.get(cacheKey);
    if (cached != null) {
        if (cached == NULL_TRANSACTION_ATTRIBUTE) {
            return null;
        }
        else {
            return cached;
        }
    }
    else {
        // We need to work it out.
        // 没有取到事务定义信息,则需要根据方法和所在类的信息,构造事务定义信息
        TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
        // Put it in the cache.
        // 无论找到没找到,最终都会将该方法缓存至attributeCache中
        if (txAttr == null) {
            this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
        }
        else {
            String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
            if (txAttr instanceof DefaultTransactionAttribute) {
                ((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);
            }
            // logger ......
            this.attributeCache.put(cacheKey, txAttr);
        }
        return txAttr;
    }
}

纵观整个方法,它要做的就是根据一个方法和方法所在的类,获取并缓存对应的事务定义信息,如果没有事务定义信息,则会缓存 NULL_TRANSACTION_ATTRIBUTE 空定义并返回。

不过这里面有一个问题,当我们 Debug 至此的时候,发现缓存中已经可以取到 TransactionAttribute 了:

Spring Dao编程原理-声明式事务的控制原理

一脸问号???这才刚开始执行方法呢,它咋就把这个方法的事务定义信息都拿到了?而且还都是对的?不对劲,它是不是在我调用 Service 之前,就已经偷偷准备好了?(就跟初始化 IOC 容器,它自己就 refresh 了一样?)

2.1.1.1 重新Debug观察

将断点打在 getTransactionAttribute 方法的第一行,重新 Debug 一下,会发现它先进入到刚打的断点中了:

Spring Dao编程原理-声明式事务的控制原理

而此时解析的 method ,竟然是在配置类中声明的那个 JdbcTemplate :

Spring Dao编程原理-声明式事务的控制原理

是不是更一脸问号了???它咋连这玩意都解析呢?哎,往上翻调用的方法栈,我们可以发现栈帧中有一个 wrapIfNecessary 方法!很明显它在搞 AOP 吧!而事务的增强,不也是利用 AOP 织入事务的通知吗?那这一切都说得过去了:由于事务通知的织入需要对每个正在创建的 bean 进行匹配,而匹配的时候需要用到 TransactionAttributeSource 去检查方法上,或者方法所在的类上是否标注有 @Transactional 注解,以此来判断是否需要对当前正在创建的 bean 织入事务通知。而不管最终事务通知是否织入进去,TransactionAttributeSource 中都会留下这些方法的判断痕迹。

2.1.1.2 设置条件断点观察

既然缘由已经搞明白了,那接下来咱只需要关注 AccountService 中的方法检查即可。借助 IDEA ,在断点处右键即可设置条件断点,这里咱声明断点的生效条件事 targetClass 是 AccountService :

Spring Dao编程原理-声明式事务的控制原理

重新 Debug ,等程序停在断点处时,也拿到了应该拿到的 transfer 方法:

Spring Dao编程原理-声明式事务的控制原理

然后,接下来理所应当的要执行 computeTransactionAttribute 方法了。

2.1.1.3 搜寻@Transactional注解

小册只截取 computeTransactionAttribute 方法最重要的部分啦,这部分有两个重要的环节:寻找方法上的 @Transactional 注解,以及寻找类上的 @Transactional 注解。

protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    // Don't allow no-public methods as required.
    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
        return null;
    }
    Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);

    // First try is the method in the target class.
    // 首先,寻找方法上是否标注了@Transactional注解
    TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
    if (txAttr != null) {
        return txAttr;
    }

    // Second try is the transaction attribute on the target class.
    // 如果方法上没有,则接下来寻找类上是否标注了@Transactional注解
    txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
    if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
        return txAttr;
    }

    // ......

    return null;
}

自上而下,先找方法级别后找类级别,可见方法级别的优先级会更高。

2.1.1.4 由parser解析事务注解

接下来,解析方法或者类上的 @Transactional 注解,调用的 findTransactionAttribute 方法的逻辑如下:

protected TransactionAttribute findTransactionAttribute(Method method) {
    return determineTransactionAttribute(method);
}

protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
    for (TransactionAnnotationParser parser : this.annotationParsers) {
        TransactionAttribute attr = parser.parseTransactionAnnotation(element);
        if (attr != null) {
            return attr;
        }
    }
    return null;
}

可以发现,解析事务注解是 TransactionAttributeSource 委托 TransactionAnnotationParser 来解析,而 Debug 至此处,发现只有一个解析器:

Spring Dao编程原理-声明式事务的控制原理

为啥只有一个,咱可以从 AnnotationTransactionAttributeSource 的构造方法中找到端倪:

public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
    this.publicMethodsOnly = publicMethodsOnly;
    // 有JTA或者EJB的依赖,则会添加额外的解析器
    if (jta12Present || ejb3Present) {
        this.annotationParsers = new LinkedHashSet<>(4);
        this.annotationParsers.add(new SpringTransactionAnnotationParser());
        if (jta12Present) {
            this.annotationParsers.add(new JtaTransactionAnnotationParser());
        }
        if (ejb3Present) {
            this.annotationParsers.add(new Ejb3TransactionAnnotationParser());
        }
    }
    else {
        // 否则,只添加一个SpringTransactionAnnotationParser
        this.annotationParsers = Collections.singleton(new SpringTransactionAnnotationParser());
    }
}

所以由此看来,AnnotationTransactionAttributeSource 中只会解析 SpringFramework 的 @Transactional 注解,确实只需要一个 SpringTransactionAnnotationParser 就够用了。

2.1.1.5 解析事务注解的动作

接下来就是让 SpringTransactionAnnotationParser 解析 @Transactional 注解的逻辑了,这部分的逻辑也相对容易理解:

@Override
@Nullable
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
    AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
            element, Transactional.class, false, false);
    if (attributes != null) {
        return parseTransactionAnnotation(attributes);
    } else {
        return null;
    }
}

解析之前,它会先判断一下是否标注有 @Transactional 注解,如果没有,直接返回空(可能出意外了?);有注解才会继续向里面解析。

接下来的解析逻辑,想必小册不标注任何注释,小伙伴们自己也能完全看得懂吧!

protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
    RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();

    Propagation propagation = attributes.getEnum("propagation");
    rbta.setPropagationBehavior(propagation.value());
    Isolation isolation = attributes.getEnum("isolation");
    rbta.setIsolationLevel(isolation.value());
    rbta.setTimeout(attributes.getNumber("timeout").intValue());
    rbta.setReadOnly(attributes.getBoolean("readOnly"));
    rbta.setQualifier(attributes.getString("value"));

    List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
    for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
        rollbackRules.add(new RollbackRuleAttribute(rbRule));
    }
    for (String rbRule : attributes.getStringArray("rollbackForClassName")) {
        rollbackRules.add(new RollbackRuleAttribute(rbRule));
    }
    for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {
        rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
    }
    for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {
        rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
    }
    rbta.setRollbackRules(rollbackRules);

    return rbta;
}

嚯,真是把 @Transactional 注解中,所有的属性都解析了一遍呢。解析完成之后,它返回了一个 RuleBasedTransactionAttribute 的对象,这个家伙实现了 TransactionAttribute 接口,自然也就实现了 TransactionDefinition 接口,所以通过这样一顿解析,就可以得到事务的定义信息了。

2.1.1.6 小结

简单总结一下这个阶段的工作内容:当应用启动的时候,由于声明了 @EnableTransactionManagement 注解,它会向 IOC 容器中注册事务通知增强器,这个增强器会参与到 bean 初始化的 AOP 后置处理逻辑中,检查被创建的 bean 中是否可以织入事务通知(标注 @Transactional 注解)。而这个检查的动作顺便保存到了 AbstractFallbackTransactionAttributeSource 的本地缓存中,所以在真正触发事务拦截器的逻辑,需要取出事务定义信息时,就可以直接从缓存中取出,而不需要重新解析了。

2.1.2 获取事务管理器

获取到事务定义信息之后,接下来要做的是获取事务管理器,执行 determineTransactionManager 方法。

protected TransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {
    // Do not attempt to lookup tx manager if no tx attributes are set
    if (txAttr == null || this.beanFactory == null) {
        return getTransactionManager();
    }

    String qualifier = txAttr.getQualifier();
    if (StringUtils.hasText(qualifier)) {
        return determineQualifiedTransactionManager(this.beanFactory, qualifier);
    }
    else if (StringUtils.hasText(this.transactionManagerBeanName)) {
        return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);
    }
    else {
        TransactionManager defaultTransactionManager = getTransactionManager();
        if (defaultTransactionManager == null) {
            defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
            if (defaultTransactionManager == null) {
                defaultTransactionManager = this.beanFactory.getBean(TransactionManager.class);
                this.transactionManagerCache.putIfAbsent(
                        DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
            }
        }
        return defaultTransactionManager;
    }
}

在这个方法中,可以发现获取的方式还是有很多种的,然而实际 Debug 的时候,发现它走投无路,到了最后、也是最里层的一步,不得已从 BeanFactory 中获取事务管理器:

Spring Dao编程原理-声明式事务的控制原理

当然,这一步肯定是能获取到之前注册的 DataSourceTransactionManager ,要是这都获取不到的话,那就见了鬼了,@EnableTransactionManagement 的部分就应该挂掉了,哪还能继续往下走呢(滑稽)。

2.2 响应式事务管理器的处理

接下来,本应该是开始处理事务的部分了,然而 SpringFramework 5.2 之后引入了响应式事务的概念,所以这里有一个响应式事务管理器的判断和处理:

    // ......
    if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
        ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> {
            if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
                throw new TransactionUsageException(
                        "Unsupported annotated transaction on suspending function detected: " + method +
                        ". Use TransactionalOperator.transactional extensions instead.");
            }
            ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(method.getReturnType());
            if (adapter == null) {
                throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " +
                        method.getReturnType());
            }
            return new ReactiveTransactionSupport(adapter);
        });
        return txSupport.invokeWithinTransaction(
                method, targetClass, invocation, txAttr, (ReactiveTransactionManager) tm);
    }
    // ......

当然,话又说回来,咱又不研究响应式事务,而且还没有大规模用于生产,所以这部分咱就跳过了。

2.3 事务控制核心

接下来要进入事务控制的核心了,通过这部分的源码,可以发现敢情就是一环绕通知呗:

    // ......
    PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

    if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
        // Standard transaction demarcation with getTransaction and commit/rollback calls.
        // 1. 开启事务
        TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

        Object retVal;
        try {
            // This is an around advice: Invoke the next interceptor in the chain.
            // This will normally result in a target object being invoked.
            // 2. 环绕通知执行Service方法
            retVal = invocation.proceedWithInvocation();
        }
        catch (Throwable ex) {
            // target invocation exception
            // 3. 捕捉到异常,回滚事务
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        }
        finally {
            cleanupTransactionInfo(txInfo);
        }

        if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
            // Set rollback-only in case of Vavr failure matching our rollback rules...
            TransactionStatus status = txInfo.getTransactionStatus();
            if (status != null && txAttr != null) {
                retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
            }
        }

        // 4. Service方法执行成功,提交事务
        commitTransactionAfterReturning(txInfo);
        return retVal;
    }
    // 下面的逻辑基本一致,不再展开 ......

核心动作就 4 步:开启事务、执行 Service 方法、遇到异常就回滚事务、没有异常就提交事务,这套路从原生的 jdbc 事务就是这么干的,合着这么牛的框架,底层也就是这么干。。。可见基础知识和理论还是最重要的呀!

下面咱分别通过一个正常的例子,和抛出异常的例子来测试成功的事务提交,和异常的事务回滚。

2.3.1 成功的事务提交

将 AccountService 的 transfer 方法中,int i = 1 / 0; 的除零异常注释掉,使该方法能正常执行完成。

回到 TransactionInterceptor 中,在 createTransactionIfNecessary 方法上打断点,重新 Debug ,等程序运行停在断点上时,接下来咱的四部曲开始。

2.3.1.1 创建事务

首先,让 createTransactionIfNecessary 方法执行过去,观察 txInfo 的属性:

Spring Dao编程原理-声明式事务的控制原理

可以发现,事务的定义信息、事务的状态都在此封装好了,此时 completed 为 false 。

2.3.1.2 执行Service方法

直接往下执行代码,让 Service 方法执行。由于除零异常被注释掉了,所以可以正常执行,并产生返回值。不过 transfer 方法的返回值是 void ,所以此处的返回值是 null 。

Spring Dao编程原理-声明式事务的控制原理

2.3.1.3 事务提交

继续往下执行,到 commitTransactionAfterReturning 方法处停止,进入方法内部:

protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
    if (txInfo != null && txInfo.getTransactionStatus() != null) {
        // logger ......
        txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
    }
}

可以发现,底层就是拿到事务管理器,执行 commit 方法而已。

而 commit 方法的底层实现要跳转到 AbstractPlatformTransactionManager 中,具体如下:(关键代码已标有注释)

public final void commit(TransactionStatus status) throws TransactionException {
    // 如果事务已经完成,则无法提交
    if (status.isCompleted()) {
        throw new IllegalTransactionStateException(
            "Transaction is already completed - do not call commit or rollback more than once per transaction");
    }

    DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
    // 如果事务已被标记为需要回滚,则回滚事务
    if (defStatus.isLocalRollbackOnly()) {
        // logger ......
        processRollback(defStatus, false);
        return;
    }

    // 全局事务的回滚标识判断(JTA)
    if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
        // logger ......
        processRollback(defStatus, true);
        return;
    }

    // 否则,视为正常,提交事务
    processCommit(defStatus);
}

由于当前压根没有任何错误,所以可以一路走到底,执行 processCommit 方法。

2.3.1.4 事务的真正提交动作

这个方法看上去很麻烦,里面的逻辑一套一条的,然而这里面的最最核心,就是一个以 do 开头的方法:doCommit 。

private void processCommit(DefaultTransactionStatus status) throws TransactionException {
    try {
        // ......

            if (status.hasSavepoint()) {
                // 如果当前事务存在保存点,则处理保存点的逻辑
            }
            // 对于新事务,直接提交事务即可
            else if (status.isNewTransaction()) {
                // logger ......
                unexpectedRollback = status.isGlobalRollbackOnly();
                doCommit(status);
            }
    // ......
}

接下来,要来到 DataSourceTransactionManager 了,因为这里面才有关于 jdbc 提交事务的动作,也是这个 doCommit 方法的实现之地。

@Override
protected void doCommit(DefaultTransactionStatus status) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
    Connection con = txObject.getConnectionHolder().getConnection();
    // logger ......
    try {
        con.commit();
    } // catch ......
}

可以发现,终于遇到我们最最熟悉的 jdbc 操作了!!!拿到 Connection 后,执行 commit 方法!!!

哇塞,终于找到提交的核心了,是不是超级兴奋,有种憋了好长时间的气终于撒开的感觉?有木有?

2.3.2 异常的事务回滚

异常的回滚,需要把之前注释掉的除零异常还原回来。

重新 Debug 时,这次不会再运行成功了,因为进入到 catch 块了:

    try {
        // 2. 环绕通知执行Service方法
        retVal = invocation.proceedWithInvocation();
    }
    catch (Throwable ex) {
        // 3. 捕捉到异常,回滚事务
        completeTransactionAfterThrowing(txInfo, ex);
        throw ex;
    }

当断点进入到 catch 块中,此时的 Debug 状态应该是这样的:(可以发现有 / by zero 的异常被捕捉到了)

Spring Dao编程原理-声明式事务的控制原理

接下来咱进入回滚的逻辑。

2.3.2.1 事务的回滚

这个回滚的逻辑,可能跟我们印象中不是很一致,咱先看一眼源码的逻辑:

protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
    if (txInfo != null && txInfo.getTransactionStatus() != null) {
        // logger ......
        // 如果当前异常在回滚范围之内,则会调用事务管理器,回滚事务
        if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
            try {
                txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
            } // catch ......
        }
        else {
            // 如果不在回滚的异常范围内,则依然会提交事务
            try {
                txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
            } // catch ......
        }
    }
}

// 默认的事务回滚捕捉异常范围
public boolean rollbackOn(Throwable ex) {
    return (ex instanceof RuntimeException || ex instanceof Error);
}

可以发现这里面的设计果然跟我们的猜测或者臆想有出入,因为默认情况下咱说过它回滚的范围是 Error 和 RuntimeException (如上面源码中的下半部分),那对于普通的 Exception ,它不会捕捉,但又出现了异常,又不捕捉,SpringFramework 的选择是不管,继续提交事务。

所以由此也提醒各位,在声明事务注解,或者 xml 配置时,一定记得声明事务回滚的异常类型。

2.3.2.2 事务的真正回滚动作

接下来,又要来到事务管理器的回滚动作了,这里面的设计就没有前面提交那么复杂了,因为回滚嘛,相对还是比较无脑的。

public final void rollback(TransactionStatus status) throws TransactionException {
    // 如果事务已经完成,则无法继续回滚
    if (status.isCompleted()) {
        throw new IllegalTransactionStateException(
            "Transaction is already completed - do not call commit or rollback more than once per transaction");
    }

    DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
    // 回滚事务
    processRollback(defStatus, false);
}

继续往下走,进入 processRollback 方法中:

private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
    try {
        // ......
            // 如果存在保存点,则直接回滚到保存点位置
            if (status.hasSavepoint()) {
                // logger ......
                status.rollbackToHeldSavepoint();
            }
            // 对于新事务,直接回滚
            else if (status.isNewTransaction()) {
                // logger ......
                doRollback(status);
            }
    // ......
}

可以发现,这个套路的设计,跟 processCommit 几乎一模一样,那咱就没啥好犹豫的了,直接进入 doRollback 即可:

@Override
protected void doRollback(DefaultTransactionStatus status) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
    Connection con = txObject.getConnectionHolder().getConnection();
    // logger ......
    try {
        con.rollback();
    } // catch ......
}

嚯,连 doRollback 中的步调都是如此的一致,果然又是原生 jdbc 的事务操作,拿到 Connection ,执行 rollback 方法回滚事务

2.4 事务执行的后处理

无论是事务的提交,还是回滚,在最后都有这样一句话:

    finally {
        cleanupAfterCompletion(status);
    }
}

这个步骤是用来清理整个事务执行过程中的 ConnectionHolder 等东西,以及解除线程中同步的事务信息。这个 cleanupAfterCompletion 方法的源码如下:

private void cleanupAfterCompletion(DefaultTransactionStatus status) {
    status.setCompleted();
    if (status.isNewSynchronization()) {
        TransactionSynchronizationManager.clear();
    }
    if (status.isNewTransaction()) {
        doCleanupAfterCompletion(status.getTransaction());
    }
    if (status.getSuspendedResources() != null) {
        // logger ......
        Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
        resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
    }
}

上面的两个 if 部分,都是清除相关的工作,感兴趣的小伙伴可以自行点进去看源码,逻辑都很简单;而最后一个 if ,有一个 resume 的动作,它的作用是释放挂起的事务,这个逻辑咱放到下一章事务传播行为中再解释。

至此,整个事务控制的逻辑也就全部理清楚了,这里面的底层控制原理我们也算是完整的走了一遍,也都摸索的差不多。

不过前面有一个方法还没有研究到:createTransactionIfNecessary 方法,它会创建一个事务诶!为啥刚才不研究它?哎,那是因为,在不同的事务传播行为下,它的处理方式是不一样的。所以这个方法咱放到下一章,与事务传播行为一起研究。

免责声明:
1.本站所有内容由本站原创、网络转载、消息撰写、网友投稿等几部分组成。
2.本站原创文字内容若未经特别声明,则遵循协议CC3.0共享协议,转载请务必注明原文链接。
3.本站部分来源于网络转载的文章信息是出于传递更多信息之目的,不意味着赞同其观点。
4.本站所有源码与软件均为原作者提供,仅供学习和研究使用。
5.如您对本网站的相关版权有任何异议,或者认为侵犯了您的合法权益,请及时通知我们处理。
火焰兔 » Spring Dao编程原理-声明式事务的控制原理