Spring @Transactional 注解是如何执行事务的?

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 相信小伙伴一定用过 @Transactional 注解,那 @Transactional 背后的秘密又知道多少呢?Spring 是如何开启事务的?又是如何进行提交事务和关闭事务的呢?

前言


相信小伙伴一定用过 @Transactional 注解,那 @Transactional 背后的秘密又知道多少呢?

Spring 是如何开启事务的?又是如何进行提交事务和关闭事务的呢?


画图猜测

在开始 debug 阅读源码之前,小伙伴们应该已经知道 MySQL 是如何开启事务的

因此可以得出猜测:

网络异常,图片无法展示
|

那下面跟着源码一起读一读,Spring 的 @Transactional 注解是如何执行事务逻辑的?


Spring 事务执行流程


开启事务

这里使用的是 Spring Boot + MySQL + Druid

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.6</version>
</dependency>

网络异常,图片无法展示
|

  1. 在创建 Bean 的时候,会对 UserService 基于 AOP 生成代理对象;

AbstractAutowireCapableBeanFactory#initializeBean ... wrapIfNecessary AbstractAutoProxyCreator#createProxy CglibAopProxy#getProxy 生成代理对象

  • 开始执行 userService.updateUserInfo(); 这里的 userService 就是代理对象;会被 CglibAopProxy.DynamicAdvisedInterceptor#intercept 方法拦截;
  • TransactionInterceptor#invoke 被事务拦截器拦截
  • TransactionAspectSupport#invokeWithinTransaction 事务处理
  • AbstractPlatformTransactionManager#getTransaction 会在这里调用 AbstractPlatformTransactionManager#startTransaction 方法,来开启事务。

网络异常,图片无法展示
|

是不是看到 doBegin 这个词突然感觉很熟悉。

跟进 DataSourceTransactionManager#doBegin 方法,注意看,此时是在 spring-jdbc-5.3.8.jar 包下面的。

网络异常,图片无法展示
|

因为使用的 druid 连接池,所以这块 Connection 是 durid 的连接池。

  • DruidPooledConnection#setAutoCommit(false) 关闭自动提交;

这里就是 druid 的逻辑,一顿执行然后到 com.alibaba.druid.filter.FilterChainImpl#connection_setAutoCommit

网络异常,图片无法展示
|

  • ConnectionImpl#setAutoCommit,这个是在 mysql-connector-java-8.0.25.jar 包下的。

网络异常,图片无法展示
|

这一句才是重点 SET autocommit=0

SET autocommit=0

开启事务了!


总结一下流程:

网络异常,图片无法展示
|


执行 SQL

在开始事务之后,会通过回调执行方法的内部逻辑。

网络异常,图片无法展示
|

  • 因为这里使用的是 Mybatis,所以还是会被代理,MapperProxy#invoke
  • DruidPooledPreparedStatement#execute
  • ClientPreparedStatement#execute

网络异常,图片无法展示
|

执行过程相对比较简单:

网络异常,图片无法展示
|

提交事务

TransactionAspectSupport#invokeWithinTransaction 最后一行,commitTransactionAfterReturning(txInfo); 就是提交事务。

  • AbstractPlatformTransactionManager#commit 抽象事务管理器,进行提交事务
  • DataSourceTransactionManager#doCommit 数据源数据管理器,提交事务

网络异常,图片无法展示
|

这里肯定是调用连接池的方法,所以会执行到 DruidPooledConnection

  • DruidPooledConnection commit
  • 最终还是执行到 mysql-connector-java-8.0.25.jar 包下面的 ConnectionImpl#commit

网络异常,图片无法展示
|


调用 commit 提交事务。

commit

网络异常,图片无法展示
|


异常回滚

异常在这里 TransactionAspectSupport#invokeWithinTransaction 会被 catch。

网络异常,图片无法展示
|

AbstractPlatformTransactionManager#rollback 在这里进行 rollback

网络异常,图片无法展示
|

执行 DataSourceTransactionManager#doRollback

最终执行到  mysql-connector-java-8.0.25.jarConnectionImpl#rollback()ConnectionImpl#rollbackNoChecks

网络异常,图片无法展示
|

从而执行 rollback 语句

rollback

网络异常,图片无法展示
|


恢复 autocommit

cleanupTransactionInfo(txInfo);

在 这个方法中会将之前设置的 autocommit 进行恢复。


Java 原生开启事务

如果觉得这样有点绕,那咱们可以看简单版本的,不带 Spring。

/**
 * @author liuzhihang
 * @date 2021/6/18 16:51
 */
public class MainTest {
    public static void main(String[] args) throws Exception {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/demo");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        Connection connection = dataSource.getConnection();
        try {
            // 关闭自动提交
            connection.setAutoCommit(false);
            connection.prepareStatement("update user_info set user_name = 'liuzhihang' where user_id = '1001';").executeUpdate();
            connection.prepareStatement("update user_address set address = 'anhui' where user_id = '1001';").executeUpdate();
            // 提交事务
            connection.commit();
        } catch (Exception e) {
            // 回滚
            connection.rollback();
        } finally {
            // 开启自动提交
            connection.setAutoCommit(true);
        }
    }
}

看完 Java 原生提交事务的方式,是不是感觉简单明了。

Spring @Transactional 只是创建了 AOP 代理,通过代理调用原生的开启关闭事务,同样在执行 SQL 那一块,也是 Mybatis 进行了代理,从而提交 SQL。


总结


最后,将图进行合并,总结流程。

网络异常,图片无法展示
|

至此,事务执行过程分析完毕。

不过还是有一个疑问?

为什么使用 set autocommit = 0

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
1月前
|
XML Java 数据格式
SpringBoot入门(8) - 开发中还有哪些常用注解
SpringBoot入门(8) - 开发中还有哪些常用注解
52 0
|
23天前
|
缓存 安全 Java
Spring高手之路26——全方位掌握事务监听器
本文深入探讨了Spring事务监听器的设计与实现,包括通过TransactionSynchronization接口和@TransactionalEventListener注解实现事务监听器的方法,并通过实例详细展示了如何在事务生命周期的不同阶段执行自定义逻辑,提供了实际应用场景中的最佳实践。
42 2
Spring高手之路26——全方位掌握事务监听器
|
24天前
|
Java 关系型数据库 数据库
京东面试:聊聊Spring事务?Spring事务的10种失效场景?加入型传播和嵌套型传播有什么区别?
45岁老架构师尼恩分享了Spring事务的核心知识点,包括事务的两种管理方式(编程式和声明式)、@Transactional注解的五大属性(transactionManager、propagation、isolation、timeout、readOnly、rollbackFor)、事务的七种传播行为、事务隔离级别及其与数据库隔离级别的关系,以及Spring事务的10种失效场景。尼恩还强调了面试中如何给出高质量答案,推荐阅读《尼恩Java面试宝典PDF》以提升面试表现。更多技术资料可在公众号【技术自由圈】获取。
|
27天前
|
前端开发 Java Spring
Spring MVC核心:深入理解@RequestMapping注解
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的核心,它将HTTP请求映射到控制器的处理方法上。本文将深入探讨`@RequestMapping`注解的各个方面,包括其注解的使用方法、如何与Spring MVC的其他组件协同工作,以及在实际开发中的应用案例。
40 4
|
27天前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
95 2
|
27天前
|
前端开发 Java Spring
探索Spring MVC:@Controller注解的全面解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序的基石之一。它不仅简化了控制器的定义,还提供了一种优雅的方式来处理HTTP请求。本文将全面解析`@Controller`注解,包括其定义、用法、以及在Spring MVC中的作用。
45 2
|
28天前
|
JavaScript Java 关系型数据库
Spring事务失效的8种场景
本文总结了使用 @Transactional 注解时事务可能失效的几种情况,包括数据库引擎不支持事务、类未被 Spring 管理、方法非 public、自身调用、未配置事务管理器、设置为不支持事务、异常未抛出及异常类型不匹配等。针对这些情况,文章提供了相应的解决建议,帮助开发者排查和解决事务不生效的问题。
|
1月前
|
消息中间件 Java 数据库
解密Spring Boot:深入理解条件装配与条件注解
Spring Boot中的条件装配与条件注解提供了强大的工具,使得应用程序可以根据不同的条件动态装配Bean,从而实现灵活的配置和管理。通过合理使用这些条件注解,开发者可以根据实际需求动态调整应用的行为,提升代码的可维护性和可扩展性。希望本文能够帮助你深入理解Spring Boot中的条件装配与条件注解,在实际开发中更好地应用这些功能。
36 2
|
1月前
|
JSON Java 数据格式
springboot常用注解
@RestController :修饰类,该控制器会返回Json数据 @RequestMapping(“/path”) :修饰类,该控制器的请求路径 @Autowired : 修饰属性,按照类型进行依赖注入 @PathVariable : 修饰参数,将路径值映射到参数上 @ResponseBody :修饰方法,该方法会返回Json数据 @RequestBody(需要使用Post提交方式) :修饰参数,将Json数据封装到对应参数中 @Controller@Service@Compont: 将类注册到ioc容器
|
1月前
|
XML Java 数据格式
SpringBoot入门(8) - 开发中还有哪些常用注解
SpringBoot入门(8) - 开发中还有哪些常用注解
43 2