前言

相信小伙伴一定用过 @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>

  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 @Transaction 只是创建了 AOP 代理,通过代理调用原生的开启关闭事务,同样在执行 SQL 那一块,也是 Mybatis 进行了代理,从而提交 SQL。

总结

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

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

不过还是有一个疑问?

为什么使用 set autocommit = 0 来开启事务,而不是使用 begin 或者 start transaction 来开启事务呢?

相关推荐

Spring @Transaction 注解是如何执行事务的?的更多相关文章

  1. Spring使用注解方式就行事务管理

    使用步骤: 步骤一.在spring配置文件中引入<tx:>命名空间<beans xmlns="http://www.springframework.org/schema/b ...

  2. spring基于注解的声明式事务控制

    package com.hope.service.impl;import com.hope.dao.IAccountDao;import com.hope.domain.Account;import ...

  3. 阶段3 2.Spring_10.Spring中事务控制_7 spring基于注解的声明式事务控制

    创建新项目 复制上一个pom.xml的内容.依赖和打包的方式 再复制src的代码过来 bean.xml.多导入context的声明 Service的实现类增加注解 dao的set方法删掉 通过Auto ...

  4. Spring整合JMS(四)——事务管理

    原文链接:http://haohaoxuexi.iteye.com/blog/1983532 Spring提供了一个JmsTransactionManager用于对JMS ConnectionFact ...

  5. Spring整合JMS(四)——事务管理(转)

    *注:别人那复制来的 Spring提供了一个JmsTransactionManager用于对JMS ConnectionFactory做事务管理.这将允许JMS应用利用Spring的事务管理特性.Jm ...

  6. spring事物配置,声明式事务管理和基于@Transactional注解的使用

    http://blog.csdn.net/bao19901210/article/details/41724355 http://www.cnblogs.com/leiOOlei/p/3725911. ...

  7. spring注解实现业务层事务管理,当业务层自调用时,事务失效问题解决

    前些日子一朋友在需要在目标对象中进行自我调用,且需要实施相应的事务定义,且网上的一种通过BeanPostProcessor的解决方案是存在问题的.因此专门写此篇帖子分析why. 1.预备知识 aop概 ...

  8. Spring 使用注解对事务控制详解与实例

    1.什么是事务 一荣俱荣,一损俱损,很多复杂的操作我们可以把它看成是一个整体,要么同时成功,要么同时失败. 事务的四个特征ACID: 原子性(Atomic):表示组成一个事务的多个数据库的操作的不可分 ...

  9. Spring系列之事务的控制 注解实现+xml实现+事务的隔离等级

    Spring系列之事务的控制 注解实现+xml实现 在前面我写过一篇关于事务的文章,大家可以先去看看那一篇再看这一篇,学习起来会更加得心应手 链接:https://blog.csdn.net/pjh8 ...

随机推荐

  1. Mybatis学习之自定义持久层框架(四) 自定义持久层框架:生产sqlSession

    前言 上一回我们完成了数据库配置文件的读取和解析工作,有了这些准备工作,我们就可以与数据库创建连接和会话了,所谓sqlSession就是数据库的会话,一切增删查改操作都是在与数据库的会话中完成,下面我 ...

  2. 看雪加密解密第一个traceme程序破解

    工具:ollydbg(吾爱破解2.10版) 工具设置:因为traceme是一个win32图形用户程序,所以其程序入口点在WinMain()函数处,设置ollydbg的调试设置的事件选项,选中在WinM ...

  3. c#基于supersocket的简单websocket服务端收发消息实现

    using log4net; using SuperSocket.SocketBase; using SuperSocket.WebSocket; using System; using System ...

  4. Linux内存带宽的一些测试笔记

    Linux内存带宽的一些测试笔记 首页 所有文章 2014年10月 GNU/Linux系统  2014-10-21 13:20  GNU/LINUX系统 内存带宽 1k 字  669 次 最近要测一下 ...

  5. Spring IoC容器 XML 配置与加载

    IoC 容器 XML 配置文件 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=&qu ...

  6. selenium多表单切换以及多窗口切换、警告窗处理

    selenium表单切换 在做UI自动化,有时候要定位的元素属性在页面上明明是唯一的.却怎么也不执行对元素的操作动作,这时候多半是iframe表单在作怪. 切入表单:iddriver.switch_t ...

  7. [TcaplusDB知识库]TcaplusDB客户端及常用命令

    数据库大都支持命令行访问,TcaplusDB也不例外,通过安装TcaplusDB客户端,您可以方便快捷地从命令行管理与访问数据库. TcaplusDB客户端 首先在 TcaplusServiceApi ...

  8. thinkphp api接口 统一结果返回处理类

    20210602 修正 wqy的笔记:http://www.upwqy.com/details/216.html 返回结果处理,归根结底 主要是有两点 数据结构和返回的数据类型 1.数据类型 :一般情 ...

  9. oracle实现通过logminer实现日志抓取分析

    场景:现场库到前置库. 思考:使用触发器? 1.侵入性解决方案 2.需要时各种配置,不需要时又是各种配置 Change Data Capture:捕捉变化的数据,通过日志监测并捕获数据库的变动(包括数 ...

  10. 如何去掉a标签的下划线

    首先来了解下<a>标签的一些样式: <a>标签的伪类样式 一组专门的预定义的类称为伪类,主要用来处理超链接的状态.超链接文字的状态可以通过伪类选择符+样式规则来控制.伪类选择符 ...