Spring5源码(44)-@Transactional声明式事物(二)事物创建简析

简介: Spring5源码(44)-@Transactional声明式事物(二)事物创建简析


1.引

上一节分析了Spring实现事物管理的步骤,本篇分析Spring事物的创建过程。

2.事物创建方法简析

protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
                                                           @Nullable TransactionAttribute txAttr,
                                                           final String joinpointIdentification) {
    // If no name specified, apply method identification as transaction name.
    // 1. 如果没有指定名称,则应用方法标识作为事务名称。
    if (txAttr != null && txAttr.getName() == null) {
        txAttr = new DelegatingTransactionAttribute(txAttr) {
            @Override
            public String getName() {
                return joinpointIdentification;
            }
        };
    }
    // 2.获取TransactionStatus对象
    TransactionStatus status = null;
    if (txAttr != null) {
        if (tm != null) {
            // 重点: 根据指定的传播行为,返回当前活动的事务或创建新事务。
            status = tm.getTransaction(txAttr);
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
                        "] because no transaction manager has been configured");
            }
        }
    }
    // 3.创建TransactionInfo对象
    return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}

最重要的是TransactionStatus对象的获取过程,以及创建TransactionInfo对象,下面分别单独介绍。

3.获取TransactionStatus对象,即Spring事物创建过程简析

public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
    // 1.获取当前事物对象(如果当前已经存在了事物)
    Object transaction = doGetTransaction();
    // Cache debug flag to avoid repeated checks.
    boolean debugEnabled = logger.isDebugEnabled();
    // 如果TransactionDefinition为空,默认创建DefaultTransactionDefinition对象
    if (definition == null) {
        // Use defaults if no transaction definition given.
        definition = new DefaultTransactionDefinition();
    }
    // 2.如果当前已经存在事物
    // 重点:
    // 如果当前已经存在启动的事物,则根据本次要新建的事物传播特性进行评估,以决定对新事物的后续处理
    if (isExistingTransaction(transaction)) {
        // Existing transaction found -> check propagation behavior to find out how to behave.
        return handleExistingTransaction(definition, transaction, debugEnabled);
    }
    // 3.如果当前不存在事物
    // Check definition settings for new transaction.
    // 3.1 如果事物定义的超时时间,小于默认的超时时间,抛出异常,TransactionDefinition.TIMEOUT_DEFAULT --> -1
    if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
        throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
    }
    // No existing transaction found -> check propagation behavior to find out how to proceed.
    // 3.2 如果当前事物特性为PROPAGATION_MANDATORY,则抛出异常(因为当前事物还没创建结束并开启...)
    // PROPAGATION_MANDATORY --> 使用当前事物,如果当前没有事物,则抛出异常。
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
        throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");
    }
    // 3.3 如果事物传播特性为以下三种,则创建新的事物:
    // PROPAGATION_REQUIRED --> 如果当前没有事物,则新建一个事物;如果已经存在一个事物,则加入到这个事物中。
    // PROPAGATION_REQUIRES_NEW --> 新建事物,如果当前已经存在事物,则挂起当前事物。
    // PROPAGATION_NESTED --> 如果当前存在事物,则在嵌套事物内执行;如果当前没有事物,则与PROPAGATION_REQUIRED传播特性相同
    else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED
            || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW
            || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
        SuspendedResourcesHolder suspendedResources = suspend(null);
        if (debugEnabled) {
            // 从日志打印,也可以看见当前要创建一个名为definition.getName()的新事物了...
            logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
        }
        try {
            boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
            // 创建DefaultTransactionStatus对象实例
            DefaultTransactionStatus status = newTransactionStatus(
                    definition,
                    transaction,
                    true,
                    newSynchronization,
                    debugEnabled,
                    suspendedResources);
            // 开启事物
            doBegin(transaction, definition);
            // 初始化事务同步。
            prepareSynchronization(status, definition);
            return status;
        }
        catch (RuntimeException | Error ex) {
            resume(null, suspendedResources);
            throw ex;
        }
    }
    // 3.4 对于其他的三种传播特性,无需开启新的事物
    // PROPAGATION_SUPPORTS --> 支持当前事物,如果当前没有事物,则以非事物方式执行
    // PROPAGATION_NOT_SUPPORTED --> 以非事物方式执行,如果当前存在事物,则挂起当前事物
    // PROPAGATION_NEVER --> 以非事物方式执行,如果当前存在事物,则抛出异常
    else {
        // Create "empty" transaction: no actual transaction, but potentially synchronization.
        if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
            logger.warn("Custom isolation level specified but no actual transaction initiated; "
                    + "isolation level will effectively be ignored: " + definition);
        }
        boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
        return prepareTransactionStatus(
                definition,
                null,
                true,
                newSynchronization,
                debugEnabled,
                null);
    }
}

该方法可以说是Spring事物最最关键、最最核心的方法了。对于这里比较重要的的方法,逐个分析。。。

3.1获取当前事物对象(如果当前已经存在了事物)

对于不同的事物管理器,获取的方法也是不同的,本例使用的是DataSourceTransactionManager

protected Object doGetTransaction() {
    DataSourceTransactionObject txObject = new DataSourceTransactionObject();
    // 是否允许使用保存点
    txObject.setSavepointAllowed(isNestedTransactionAllowed());
    // 从当前线程中获取ConnectionHolder对象
    ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
    txObject.setConnectionHolder(conHolder, false);
    return txObject;
}
3.2 如果当前已经存在事物

这里,涉及到事物嵌套,留在后面单独分析。。。

3.3 如果当前不存在事物

这里就不会涉及到事物的嵌套处理了,分析起来也比较简单点,接下来的处理就是要根据我们配置的事物传播特性,去逐个分析了

3.3.1 超时时间处理

很简单,如果自定义的超时时间小于默认的超时时间,则抛出异常

3.3.2 处理PROPAGATION_MANDATORY传播特性

如果我们定义的事物传播特性为PROPAGATION_MANDATORY,那么这里会抛出异常,因为当前没有事物,或者说事物还在创建中。而PROPAGATION_MANDATORY特性是:使用当前事物,如果当前没有事物,则抛出异常。

3.3.3 处理PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED传播特性

先来回顾一下这三种传播特性:

PROPAGATION_REQUIRED --> 如果当前没有事物,则新建一个事物;如果已经存在一个事物,则加入到这个事物中。

PROPAGATION_REQUIRES_NEW --> 新建事物,如果当前已经存在事物,则挂起当前事物。

PROPAGATION_NESTED --> 如果当前存在事物,则在嵌套事物内执行;如果当前没有事物,则与PROPAGATION_REQUIRED传播特性相同

因为当前不存在事物,根据这几种特性,在这里就要新建事物了,代码片段:

try {
    boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
    // 创建DefaultTransactionStatus对象实例
    DefaultTransactionStatus status = newTransactionStatus(
            definition,
            transaction,
            true,
            newSynchronization,
            debugEnabled,
            suspendedResources);
    // 开启事物
    doBegin(transaction, definition);
    // 初始化事务同步。
    prepareSynchronization(status, definition);
    return status;
}

小分支太多了,下面单独在开一篇介绍吧。。。

3.3.4 处理PROPAGATION_SUPPORTS、PROPAGATION_NOT_SUPPORTED、PROPAGATION_NEVER传播特性

回顾一下这三种特性:

PROPAGATION_SUPPORTS --> 支持当前事物,如果当前没有事物,则以非事物方式执行

PROPAGATION_NOT_SUPPORTED --> 以非事物方式执行,如果当前存在事物,则挂起当前事物

PROPAGATION_NEVER --> 以非事物方式执行,如果当前存在事物,则抛出异常

因为当前不存在事物,根据这几种特性,无需开启真正的事物。

4.创建TransactionInfo对象

这里我们假设事物已经创建完成了(上一步没有分析事物具体的创建过程,留在下一篇介绍),接下来就要创建TransactionInfo对象了,该对象持有了上一步创建的事物信息。

protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
                                                     @Nullable TransactionAttribute txAttr,
                                                     String joinpointIdentification,
                                                     @Nullable TransactionStatus status) {
    // 创建TransactionInfo对象
    TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
    // 如果事物标签不为空,则将TransactionStatus对象赋予TransactionInfo
    if (txAttr != null) {
        // We need a transaction for this method...
        if (logger.isTraceEnabled()) {
            logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");
        }
        // The transaction manager will flag an error if an incompatible tx already exists.
        txInfo.newTransactionStatus(status);
    } else {
        // The TransactionInfo.hasTransaction() method will return false. We created it only
        // to preserve the integrity of the ThreadLocal stack maintained in this class.
        if (logger.isTraceEnabled()) {
            logger.trace("Don't need to create transaction for [" + joinpointIdentification + "]: This method isn't transactional.");
        }
    }
    // 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.
    // 这里:不论是否开启了新的事物,都将TransactionInfo绑定到当前线程,以保证TransactionInfo堆栈的完整性。
    txInfo.bindToThread();
    return txInfo;
}

基本上就是简单的赋值操作,并在最后将创建的TransactionInfo绑定到当前线程。注意,在绑定过程中,当前事物信息是会获取已有的事物信息并一起绑定到transactionInfoHolder变量的。这样就可能会形成一个事物链。

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

其中transactionInfoHolder是个ThreadLocal类型的变量:

private static final ThreadLocal<TransactionInfo> transactionInfoHolder = new NamedThreadLocal<>("Current aspect-driven transaction");

本篇的分析就到此了,因为这里的分支实在是太多,还是多开几篇吧!



目录
相关文章
|
21天前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
5月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
370 70
|
5月前
|
Java 关系型数据库 MySQL
深入解析 @Transactional——Spring 事务管理的核心
本文深入解析了 Spring Boot 中 `@Transactional` 的工作机制、常见陷阱及最佳实践。作为事务管理的核心注解,`@Transactional` 确保数据库操作的原子性,避免数据不一致问题。文章通过示例讲解了其基本用法、默认回滚规则(仅未捕获的运行时异常触发回滚)、因 `try-catch` 或方法访问修饰符不当导致失效的情况,以及数据库引擎对事务的支持要求。最后总结了使用 `@Transactional` 的五大最佳实践,帮助开发者规避常见问题,提升项目稳定性与可靠性。
712 12
|
6月前
|
存储 监控 数据可视化
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
109 0
|
2月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
707 0
|
6月前
|
前端开发 Java 数据库
微服务——SpringBoot使用归纳——Spring Boot集成Thymeleaf模板引擎——Thymeleaf 介绍
本课介绍Spring Boot集成Thymeleaf模板引擎。Thymeleaf是一款现代服务器端Java模板引擎,支持Web和独立环境,可实现自然模板开发,便于团队协作。与传统JSP不同,Thymeleaf模板可以直接在浏览器中打开,方便前端人员查看静态原型。通过在HTML标签中添加扩展属性(如`th:text`),Thymeleaf能够在服务运行时动态替换内容,展示数据库中的数据,同时兼容静态页面展示,为开发带来灵活性和便利性。
289 0
|
2月前
|
缓存 JSON 前端开发
第07课:Spring Boot集成Thymeleaf模板引擎
第07课:Spring Boot集成Thymeleaf模板引擎
349 0
第07课:Spring Boot集成Thymeleaf模板引擎
|
6月前
|
XML Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——基于 xml 的整合
本教程介绍了基于XML的MyBatis整合方式。首先在`application.yml`中配置XML路径,如`classpath:mapper/*.xml`,然后创建`UserMapper.xml`文件定义SQL映射,包括`resultMap`和查询语句。通过设置`namespace`关联Mapper接口,实现如`getUserByName`的方法。Controller层调用Service完成测试,访问`/getUserByName/{name}`即可返回用户信息。为简化Mapper扫描,推荐在Spring Boot启动类用`@MapperScan`注解指定包路径避免逐个添加`@Mapper`
249 0
|
6月前
|
Java 测试技术 微服务
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——少量配置信息的情形
本课主要讲解Spring Boot项目中的属性配置方法。在实际开发中,测试与生产环境的配置往往不同,因此不应将配置信息硬编码在代码中,而应使用配置文件管理,如`application.yml`。例如,在微服务架构下,可通过配置文件设置调用其他服务的地址(如订单服务端口8002),并利用`@Value`注解在代码中读取这些配置值。这种方式使项目更灵活,便于后续修改和维护。
88 0

热门文章

最新文章