Seata1.5.2源码学习

文章有点长,我决定用半个小时来和你分享~😂 废话不多说,上代码。。。

Seata1.5.2源码学习

基于Seata 1.5.2,项目中用 seata-spring-boot-starter

  1. SeataDataSourceAutoConfiguration

Seata1.5.2源码学习

SeataDataSourceAutoConfiguration 主要是配置数据源代理,可以看到:

  1. 默认seata.enabled、seata.enableAutoDataSourceProxy、seata.enable-auto-data-source-proxy都是true
  2. 只有当classpath中有DataSource时才会进行此配置
  3. 创建了一个SeataAutoDataSourceProxyCreator,用于自动代理数据源

Seata1.5.2源码学习

先记一下,SeataAutoDataSourceProxyCreator是一个BeanPostProcessor

刚才new了一个SeataAutoDataSourceProxyCreator,继续看构造方法,默认useJdkProxy是false,excludes为空,dataSourceProxyMode是AT

Seata1.5.2源码学习

构造方法中最重要的一件事情是构造AOP通知(拦截器),这里new了一个SeataAutoDataSourceProxyAdvice

Seata1.5.2源码学习

SeataAutoDataSourceProxyAdvice是一个MethodInterceptor。

MethodInterceptor是aop中的一个接口,当目标方法被调用时就会调用与之关联的MethodInterceptor的invoke方法

Seata1.5.2源码学习

至此,在构造方法中完成了advisors的赋值,advisors[]中有一个DefaultIntroductionAdvisor,DefaultIntroductionAdvisor中引用了SeataAutoDataSourceProxyAdvice

前面说过,SeataAutoDataSourceProxyCreator是一个BeanPostProcessor,而BeanPostProcessor是BeanFactory中的一个钩子(回调),称之为后置处理器

Seata1.5.2源码学习

AbstractAutoProxyCreator#postProcessBeforeInstantiation()

AbstractAutoProxyCreator#postProcessAfterInitialization()

Seata1.5.2源码学习

AbstractAutoProxyCreator#wrapIfNecessary()

Seata1.5.2源码学习

AbstractAutoProxyCreator#getAdvicesAndAdvisorsForBean()

Seata1.5.2源码学习

SeataAutoDataSourceProxyCreator#getAdvicesAndAdvisorsForBean()

Seata1.5.2源码学习

SeataAutoDataSourceProxyCreator#wrapIfNecessary()

Seata1.5.2源码学习

DataSourceProxyHolder维护了数据源对象与数据源代理对象的映射

Seata1.5.2源码学习

至此,数据源代理部分就看完了,下面总结一下:

1、启动的时候自动配置数据源代理,创建了一个SeataAutoDataSourceProxyCreator

2、SeataAutoDataSourceProxyCreator在构造方法中创建AOP通知,并赋值给其属性

3、AbstractAutoProxyCreator是一个抽象类不能被实例化,能实例化的只有SeataAutoDataSourceProxyCreator

4、SeataAutoDataSourceProxyCreator从AbstractAutoProxyCreator那里继承了很多属性和方法,其中就包括postProcessBeforeInstantiation()、postProcessAfterInitialization()、createProxy()等等

5、SeataAutoDataSourceProxyCreator间接实现了BeanPostProcessor接口,也就是说它也是BeanPostProcessor的一个实现类

6、BeanFactory回调所有的BeanPostProcessor#postProcessAfterInitialization()时,就会调用SeataAutoDataSourceProxyCreator的postProcessAfterInitialization()方法,最终会调用wrapIfNecessary()方法

7、wrapIfNecessary()只关心DataSource对象,它负责为DataSource对象生成代理对象,并且在SeataAutoDataSourceProxyCreator中维护了DataSource对象与SeataDataSourceProxy对象之间的映射关心

8、创建代理对象时,会给DataSource对象应用AOP拦截器。用AOP的话来讲,就是给目标对象DataSource织入通知,并创建一个被增强的代理对象

9、通知(拦截器)是SeataAutoDataSourceProxyAdvice,它实现了MethodInterceptor接口

10、SeataAutoDataSourceProxyAdvice#invoke()方法所做的事情就是,拿到原始DataSource的代理对象,并且在代理对象上调用目标方法

综上所述,以上做的所有工作都是为了将来调用 javax.sql.DataSource 上的任意方法时都会被拦截,然后调用其代理对象上对应的方法。而DataSource中最重要的一个方法就是getConnection()

划重点:将来,所有调用 javax.sql.DataSource#getConnection() 都会被拦截,然后在代理对象上执行getConnection(),因此可以这样说

调 javax.sql.DataSource#getConnection() 实际上执行的是 io.seata.rm.datasource.SeataDataSourceProxy#getConnection()

  1. SeataAutoConfiguration

SeataAutoConfiguration里面主要是配置GlobalTransactionScanner(全局事务扫描器)

seata.enabled=true 才会开启 SeataAutoConfiguration

Seata1.5.2源码学习

Seata1.5.2源码学习

Seata1.5.2源码学习

Seata1.5.2源码学习

GlobalTransactionScanner 也继承自 AbstractAutoProxyCreator,同时还实现了InitializingBean接口。BeanFactory在设置了所有bean属性之后会调用InitializingBean的afterPropertiesSet()方法

GlobalTransactionScanner#afterPropertiesSet()

Seata1.5.2源码学习

Seata1.5.2源码学习

io.seata.common.DefaultValues中定义了很多默认值

Seata1.5.2源码学习

同样地,因为实现了BeanPostProcessor接口,所以在启动时BeanFactory实例化Bean之后,会调用GlobalTransactionScanner的postProcessAfterInitialization(),尽管这个postProcessAfterInitialization()方法时从AbstractAutoProxyCreator那里继承来的,但是不影响啊,还是会调用GlobalTransactionScanner这个bean的postProcessAfterInitialization()方法。于是,最终又会调wrapIfNecessary()方法。

GlobalTransactionScanner#wrapIfNecessary()

Seata1.5.2源码学习

这里面有一个很重要的逻辑就是,创建了一个GlobalTransactionalInterceptor对象,并赋值给interceptor

AbstractAutoProxyCreator#getAdvicesAndAdvisorsForBean()是一个抽象方法,实现在子类GlobalTransactionScanner中

Seata1.5.2源码学习

Seata1.5.2源码学习

Seata1.5.2源码学习

因此,所有在GlobalTransactionScanner#wrapIfNecessary()中被代理的对象,都被应用GlobalTransactionalInterceptor

GlobalTransactionalInterceptor也是一个MethodInterceptor

也就是说,目标方法的调用都会转到GlobalTransactionalInterceptor#invoke()上

Seata1.5.2源码学习

GlobalTransactionalInterceptor#handleGlobalTransaction()

Seata1.5.2源码学习

事务执行直接调用TransactionalTemplate的execute()方法

io.seata.tm.api.TransactionalTemplate#execute()

Seata1.5.2源码学习

io.seata.tm.api.GlobalTransactionContext#getCurrent() 获取当前事务

Seata1.5.2源码学习

io.seata.tm.api.TransactionalTemplate#beginTransaction()

Seata1.5.2源码学习

tx是DefaultGlobalTransaction

io.seata.tm.api.DefaultGlobalTransaction#begin()

DefaultGlobalTransaction中的TransactionManager是DefaultTransactionManager

Seata1.5.2源码学习

DefaultTransactionManager中提供了事务相关的底层操作

Seata1.5.2源码学习

io.seata.tm.api.DefaultGlobalTransaction#commit()

Seata1.5.2源码学习

io.seata.tm.api.DefaultGlobalTransaction#rollback()的逻辑与commit()类似,都是重试调用transactionManager.rollback(xid)

全局事务扫描器部分的代码就看到这里,下面总结一下:

1、配置项seata.enabled=true 会触发 SeataAutoConfiguration 自动配置

2、SeataAutoConfiguration中创建了一个GlobalTransactionScanner

3、GlobalTransactionScanner继承了AbstractAutoProxyCreator,并实现InitializingBean接口

4、初始化TM、RM

5、由于继承了AbstractAutoProxyCreator,所以BeanFactory会调用GlobalTransactionScanner#方法postProcessAfterInitialization(),最终会调用GlobalTransactionScanner#wrapIfNecessary()来为目标对象创建代理对象

6、GlobalTransactionScanner#wrapIfNecessary()中创建了一个GlobalTransactionalInterceptor,GlobalTransactionalInterceptor是一个MethodInterceptor

7、在创建代理对象的时候,在AbstractAutoProxyCreator#wrapIfNecessary()方法中,为代理对象应用GlobalTransactionalInterceptor,于是所有目标对象上的方法调用就会转为调用GlobalTransactionalInterceptor#invoke()

8、GlobalTransactionalInterceptor#invoke()方法中,首先获取被调用的目标对象的Class和Method对象,然后检查目标方法或类上是否有@GlobalTransactional或@GlobalLock注解,而且配置项中不能禁用全局事务

9、如果加了@GlobalTransactional注解,则创建一个AspectTransactional,然后开始处理全局事务,默认传播特性是REQUIRED

10、如果加了@GlobalLock注解,则开始处理全局锁

11、处理全局事务就是直接调用事务模板中的execute方法,TransactionalTemplate#execute()是一个模板方法,其中定义了事务处理的流程。首先开启事务,然后执行业务逻辑,最后提交事务,异常回滚事务。

12、事务操作是在DefaultGlobalTransaction中处理的,最终处理在DefaultTransactionManager。DefaultTransactionManager负责同步远程调用,向TC发请求来开启、提交、回滚事务等操作

Seata1.5.2源码学习
  1. 数据库操作执行SQL语句

通过Java自带的JDBC操作数据库通常是这样的:

Class.forName(driverClass);
// 获取Connection
Connection connection = DriverManager.getConnection(url,user,password);
// 创建Statement或者PreparedStatement
Statement stmt = connection.createStatement();
stmt.execute(sql);

// PreparedStatement ps = connection.prepareStatement(sql);
// ps.execute();

MyBatis底层也是这一套

接下来看Seata是如何做的

首先是获取数据库连接Connection,前面已经说过了,调用DataSource的getConnection()方法底层是在代理对象SeataDataSourceProxy上调用getConnection()。SeataDataSourceProxy是接口,如果是AT模式,则这个数据源代理对象是DataSourceProxy

Seata1.5.2源码学习

Seata1.5.2源码学习

DataSourceProxy#getConnection()获取数据库连接

Seata1.5.2源码学习

ConnectionProxy#createStatement()

Seata1.5.2源码学习

Seata1.5.2源码学习

Seata1.5.2源码学习

Seata1.5.2源码学习

ConnectionProxy#prepareStatement()

Seata1.5.2源码学习

Seata1.5.2源码学习

PreparedStatementProxy 继承自 StatementProxy,因此下面就直接看PreparedStatementProxy如何执行SQL

PreparedStatementProxy#execute()

Seata1.5.2源码学习

ExecuteTemplate#execute() 是一个模板方法

Seata1.5.2源码学习

挑一个看看吧,就挑UpdateExecutor

Seata1.5.2源码学习

Seata1.5.2源码学习

Seata1.5.2源码学习

UpdateExecutor构造方法中一直调父类的构造法,既然如此,那么直接看BaseTransactionalExecutor

Seata1.5.2源码学习

UpdateExecutor#execute()

这个方法时从BaseTransactionalExecutor那里继承来的,又是一个模板方法,可见设计模式是多么重要

Seata1.5.2源码学习

AbstractDMLBaseExecutor#doExecute()

Seata1.5.2源码学习

AbstractDMLBaseExecutor#executeAutoCommitTrue()

Seata1.5.2源码学习

ConnectionProxy#changeAutoCommit()

Seata1.5.2源码学习

现在事务自动提交已经被Seata改成false了

Seata1.5.2源码学习

UpdateExecutor#beforeImage()

Seata1.5.2源码学习

BaseTransactionalExecutor#prepareUndoLog()

Seata1.5.2源码学习

接下来,提交事务

ConnectionProxy#commit()

Seata1.5.2源码学习

ConnectionProxy#processGlobalTransactionCommit() 处理全局事务提交

Seata1.5.2源码学习

分支事务提交以后,业务数据更改和undo_log就都提交了

回想一下,为什么在执行业务修改前要先将默认的自动提交改成手动提交,最后再改成自动提交呢?

因为,要将业务数据修改和插入undo_log放在同一个事务里,一起提交

这一切都归功于代理

回顾一下整个调用链

Seata1.5.2源码学习

结合之前的案例,AT模式TC、TM、RM三者的交互应该是这样的:

Seata1.5.2源码学习

问题一:为什么在执行的时候,先将数据库自动提交autoCommit设为false,最后再改成true呢?

答:因为,需要将undo_log和业务数据修改放到同一个事务中,这样可以保证业务数据修改成功后undo_log必然插入成功,所以Seata要将其改为手动提交。最后再改成true是因为默认autoCommit就是true,这样可以不影响其它业务。

问题二:什么情况下ConnectionContext中xid=null,且isGlobalLockRequire=true呢?或者换一种问法,什么情况下不在全局事务中,当仍然需要全局锁呢?

答:当业务方法上不加@GlobalTransactional,而是只加了@GlobalLock注解的情况下,就会出现上述情况,也就会执行 ConnectionProxy#processLocalCommitWithGlobalLocks()方法,在事务提交前检查全局锁,这样设计的目的是在AT模式下,不出现脏读、脏写。由于数据源被代理了,当一个加了@GlobalTransactional的全局事务,与另一个加了@GlobalTransactional或@GlobalLock注解的事务在本地事务提交前就会检查全局锁,要先获得全局锁才能提交本地事务,这样就避免了脏读脏写,从而相当于实现了全局事务的读已提交隔离级别。参见:https://seata.io/zh-cn/blog/seata-at-lock.html

关于Seata 1.5.2 Client端的源码学习就先到这里,欢迎交流~

如果你都已经看到了这里,不妨给我点个赞吧😄

Original: https://www.cnblogs.com/cjsblog/p/16866796.html
Author: 废物大师兄
Title: Seata1.5.2源码学习

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/711822/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Worktile 技术架构概要

    其实早就该写这篇博客了,一直说忙于工作没有时间,其实时间挤挤总会有的,可能就是因为懒吧!从2013年11月一直拖到现在,今天就简单谈谈 Worktile 的技术架构吧 。 Work…

    技术杂谈 2023年5月31日
    0104
  • SSM实战(55)在线教育(55)前端(24)课程管理(10)章节添加/修改/删除

    博客园 :当前访问的博文已被密码保护 请输入阅读密码: Original: https://www.cnblogs.com/qiu-hua/p/16514337.htmlAutho…

    技术杂谈 2023年6月1日
    094
  • 保姆教程系列二、Nacos实现注册中心

    前言: 请各大网友尊重本人原创知识分享,谨记本人博客:南国以南i 上篇我们介绍到 保姆教程系列一、Linux搭建Nacos 注册中心原理 一、环境准备 Java版本:1.8+ (L…

    技术杂谈 2023年7月11日
    0130
  • Android so(ELF)文件解析

    一、前言 so文件是啥?so文件是elf文件,elf文件后缀名是 .so ,所以也被chang常称之为 so文件,elf文件是linux…

    技术杂谈 2023年7月10日
    0100
  • python中的*和**

    简介:Python中的与*操作符使用最多的就是两种用法。 1.用做运算符,即表示乘号,*表示次方。 用于指定函数传入参数的类型的。*用于参数前面,表示传入的多个参数将按照元组的形式…

    技术杂谈 2023年7月25日
    0111
  • 网站PWA升级

    前面的话 渐进式网络应用 ( Progressive Web Apps ),即我们所熟知的 PWA,是 Google 提出的用前沿的 Web 技术为网页提供 App 般使用体验的一…

    技术杂谈 2023年5月31日
    0136
  • 面试中关于字符串及常量池的一些考点

    字符串及常量池在面试中很容易被问到,前2天在为公司做校招面试时,发现很多同学对相关细节不太清楚,在此梳理一下: 先回顾一下java中字符串的设计,大家都知道jvm中有所谓的&#82…

    技术杂谈 2023年5月31日
    098
  • 触发器

    1、MySQL包含对触发器的支持。触发器是一种与表操作有关的数据库对象,当触发器所在表上出现指定事件时,将调用该对象,即表的操作事件触发表上的触发器的执行。 2、创建一个执行语句的…

    技术杂谈 2023年7月25日
    084
  • Jquery_HTML-对HTML内容删除添加、操作CSS改变样式、遍历定位元素

    1 DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset=&q…

    技术杂谈 2023年7月24日
    0104
  • Java并发编程之AQS以及源码解析

    文章目录 概览 实现思路 实现原理 * 源自CLH锁 AQS数据模型 CAS操作 主要方法 * 自定义同步器的实现方法 AQS定义的模板方法 源码解读 * 等待状态释义 AQS获取…

    技术杂谈 2023年7月24日
    0151
  • 音频和视频同步工具:Red Giant PluralEyes for Mac

    Original: https://www.cnblogs.com/aurora-123/p/16874370.htmlAuthor: 佛系女孩Title: 音频和视频同步工具:R…

    技术杂谈 2023年7月11日
    0123
  • 部署-jenkins发布项目到windows环境

    使用openSSH的方式 如果我们项目的部署环境在windows环境上,我们可以选择给服务器安装openSSH的方式,然后以脚本的方式进行部署。也可以通过web容器的对外访问地址,…

    技术杂谈 2023年7月23日
    0119
  • docker打包Python项目成镜像文件

    做实验时因为数据太大用到了学校的服务器平台,在平台进行训练的时候首先需要搭建环境,由于种种原因,只能自己用docker来打包镜像文件,自己做镜像。记录一下遇到的坑以及解决方法。 d…

    技术杂谈 2023年7月10日
    0101
  • nodejs队列

    nodejs队列 创建具有指定并发性的队列对象。添加到队列的任务以并行方式处理(直到并发性限制)。如果所有的worker都在进行中,任务就会排队,直到有一个worker可用。wor…

    技术杂谈 2023年5月31日
    0100
  • 序列化二叉树

    序列化二叉树 问题描述 请实现两个函数,分别用来序列化和反序列化二叉树。你需要设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证…

    技术杂谈 2023年7月25日
    0112
  • 关于CPU的一些基本知识总结

    关于CPU和程序的执行 CPU是计算机的大脑。 程序的运行过程,实际上是程序涉及到的、未涉及到的一大堆的指令的执行过程。 当程序要执行的部分被装载到内存后,CPU要从内存中取出指令…

    技术杂谈 2023年5月31日
    0153
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球