Spring @Transaction 注解是如何执行事务的?
前言
相信小伙伴一定用过 @Transaction 注解,那 @Transaction 背后的秘密又知道多少呢?
Spring 是如何开启事务的?又是如何进行提交事务和关闭事务的呢?
画图猜测
在开始 debug 阅读源码之前,小伙伴们应该已经知道 MySQL 是如何开启事务的。
因此可以得出猜测:

那下面跟着源码一起读一读,Spring 的 @Transaction 注解是如何执行事务逻辑的?
Spring 事务执行流程
开启事务
这里使用的是 Spring Boot + MySQL + Druid
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>

- 在创建 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.jar 的 ConnectionImpl#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 @Transaction 只是创建了 AOP 代理,通过代理调用原生的开启关闭事务,同样在执行 SQL 那一块,也是 Mybatis 进行了代理,从而提交 SQL。
总结
最后,将图进行合并,总结流程。

至此,事务执行过程分析完毕。
不过还是有一个疑问?
为什么使用 set autocommit = 0 来开启事务,而不是使用 begin 或者 start transaction 来开启事务呢?
相关推荐
Spring @Transaction 注解是如何执行事务的?的更多相关文章
- Spring使用注解方式就行事务管理
使用步骤: 步骤一.在spring配置文件中引入<tx:>命名空间<beans xmlns="http://www.springframework.org/schema/b ...
- spring基于注解的声明式事务控制
package com.hope.service.impl;import com.hope.dao.IAccountDao;import com.hope.domain.Account;import ...
- 阶段3 2.Spring_10.Spring中事务控制_7 spring基于注解的声明式事务控制
创建新项目 复制上一个pom.xml的内容.依赖和打包的方式 再复制src的代码过来 bean.xml.多导入context的声明 Service的实现类增加注解 dao的set方法删掉 通过Auto ...
- Spring整合JMS(四)——事务管理
原文链接:http://haohaoxuexi.iteye.com/blog/1983532 Spring提供了一个JmsTransactionManager用于对JMS ConnectionFact ...
- Spring整合JMS(四)——事务管理(转)
*注:别人那复制来的 Spring提供了一个JmsTransactionManager用于对JMS ConnectionFactory做事务管理.这将允许JMS应用利用Spring的事务管理特性.Jm ...
- spring事物配置,声明式事务管理和基于@Transactional注解的使用
http://blog.csdn.net/bao19901210/article/details/41724355 http://www.cnblogs.com/leiOOlei/p/3725911. ...
- spring注解实现业务层事务管理,当业务层自调用时,事务失效问题解决
前些日子一朋友在需要在目标对象中进行自我调用,且需要实施相应的事务定义,且网上的一种通过BeanPostProcessor的解决方案是存在问题的.因此专门写此篇帖子分析why. 1.预备知识 aop概 ...
- Spring 使用注解对事务控制详解与实例
1.什么是事务 一荣俱荣,一损俱损,很多复杂的操作我们可以把它看成是一个整体,要么同时成功,要么同时失败. 事务的四个特征ACID: 原子性(Atomic):表示组成一个事务的多个数据库的操作的不可分 ...
- Spring系列之事务的控制 注解实现+xml实现+事务的隔离等级
Spring系列之事务的控制 注解实现+xml实现 在前面我写过一篇关于事务的文章,大家可以先去看看那一篇再看这一篇,学习起来会更加得心应手 链接:https://blog.csdn.net/pjh8 ...
随机推荐
- 关于Redis哨兵机制,7张图详解!
写在前面 之前有位朋友去面试被问到Redis哨兵机制,这道题其实很多小伙伴都应该有被问到过!本文将跟大家一起来探讨如何回答这个问题!同时用XMind画了一张导图记录Redis的学习笔记和一些面试解析( ...
- mysql知识点归纳-执行计划篇
愿历尽千帆,归来仍是少年 缘由: 优化sql,顾此记录一下,以便温习之用. 前置: sql执行过程:客户端 -> 连接器 -> 分析器 (或查询缓存 - > end) -> 优 ...
- 列表 元组 sort
列表 增append insert extend 迭代增加删 remove delete pop clear改 li[索引]="被修改的内容":切片"" 列表 ...
- tuple必须加上逗号
tuple支持 空 元组 不加逗号 >>> tup4 = () tuple非空的元组必须加上逗号>>> tup4 = (55,)>>> tup4 ...
- 企业案例-Mysql误删除用户表如何恢复
1.不小心删除了mysql所有用户 mysql> delete from mysql.user where 1=1; Query OK, 5 rows affected (0.00 sec) # ...
- 云计算OpenStack共享组件---信息队列rabbitmq(2)
一.MQ 全称为 Message Queue, 消息队列( MQ ) 是一种应用程序对应用程序的通信方法.应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们. 消息传 ...
- 在Linux服务器,搭建K8s服务【脚本篇】
前言 好久没有写博客了,本文主要是对网上文章的总结篇,主要是将安装和运行代码做了一次真机实验,亲测可用.文章内包含的脚本和代码,多来自于网络,也有我自己的调整和配置,文章末尾对参考的文献做了列举,方便 ...
- Python数模笔记-PuLP库(1)线性规划入门
1.什么是线性规划 线性规划(Linear programming),在线性等式或不等式约束条件下求解线性目标函数的极值问题,常用于解决资源分配.生产调度和混合问题.例如: max fx = 2*x1 ...
- sublime使用与配置
目录 Download Markdown转浏览器显示 1. 简单版本 2. 有MD全格式版本 Install Package Control 删除文本空行 1. Ctrl + H 2. Find \s ...
- ubuntu设置允许root用户登录
一.允许ssh登录root用户(命令行) 1.修改root 密码,sudo passwd root 2.修改ssh配置, sudo vim /etc/ssh/shd_config,修改文件中的Perm ...