鬣狗技术- spring|spring-tx的秘密-鬣狗bot
健康生活、快乐学习、深度思考
大厂深耕多年,技术不断学习迭代并且深入研究,热爱技术,热爱开源
右上角⬆️点击关注➕,一起进步,关注作者并获取最新技术资讯和技术文章;持续更新有思考深度的文章和技术。
作者:鬣狗
日期:2021年8月22日
0.简介本篇将介绍Spring事务框架的运行原理,包括Spring事务框架实现原理、事务传播行为等
1.原理和架构Spring-Tx技术原理的本质的AOP+ThreadLocal。通过AOP技术生成拦截器对业务方法的拦截来达到事务的提交和回滚以及Spring事务传播的实现。ThreadLocal用来存放当前线程使用的数据库连接对象以及额外资源。
图1 TransactionIntecetor类继承图
如图1是类TransactionInterceptor类的继承关系图,TransactionInteceptor类的作用就是根据Spring的事务传播属性来实施具体的拦截策略。可以看到这个类实现了MethodInteceptor接口,并且继承了TransactionAspectSupport,说明TransactionIntceptor本身就是个切面。TransactionAspectSupport类作为事务切面的基类,规定了事务执行的骨架,抽象如下:
// get current transaction determined by transaction propagationTransaction tx = getTransaction();
try{
invokeMethod(); // execute method }catch(Exceptione){
rollback(); // rollback }finally{
cleanUp(); // clean up the resouce used or reset the tx-info}
// commit the statement ignore the exceptioncommitTransactionAfterReturning();step 1. 首先根据spring事务的传播属性获取当前的事务
step 2. try:在当前的事务下执行业务方法
step 3. catch:如果执行出错,则回滚事务
step 4. finally:最后不管事务是否执行成功,做一些资源清理的工作
step 5. 最后方法执行完毕后提交当前事务
以上就是spring-tx的基本思想。
图2 Spring-Tx核心代码结构
如上图是Spring-Tx的代码架构。其中PlatformTransactionManager类是Spring-Tx中的中心接口。
图3 PlatformTransactionManager类
PlatformTransactionManager类只有三个方法:
getTransaction
commit
rollback
AbstractPlatformTransactionManager继承了PlatformTransactionManager并且实现了Spring的标准事务工作流(Spring’s standard transaction workflow --- 也就是上文提到的工作流程)。并且提供以下的功能:
determines if there is an existing transaction
applies the appropriate propagation behavior
suspends and resumes transactions if necessary
checks the rollback-only flag on commit
trrigger registered synchronization callbacks if transaction synchronization is active
其中
TransactionSynchronization类似回调函数的作用,其中定义的方法有resume、suspend、beforeCommit、beforeCompetition、afterCommit、afterCompetition。这些方法被调用的时机正如其方法名称一样,例如afterCommit,这个方式是在事务提交后执行。在仔细分析spring-tx代码之前,我们首先认识下spring-tx出现的重要的类的概念:
名词
概念
PlatformTransactionManager
事务管理器,管理事务的各生命周期方法,下文简称TxMgr
TransactionAttribute
事务属性, 包含隔离级别,传播行为,是否只读等信息,下文简称TxAttr
TransactionStatus
事务状态,下文简称TxStatus
TransactionInfo
事务信息,内含TxMgr, TxAttr, TxStatus等信息,下文简称TxInfo
TransactionSynchronization
事务同步回调,内含多个钩子方法,下文简称TxSync / transaction synchronization
TransactionSynchronizationManager
事务同步管理器,维护当前线程事务资源,信息以及TxSync集合
其中TransactionAttribute和TransactionStatus去区分开来,TransactionAttribute是事务当前的属性,如隔离级别、传播属性、read-only等。而TransactionStatus表示事务的状态,如当前事务是否是新事务,事务是否有savepoint,事务是否已经完成。
备注:表格出自
https://www.cnblogs.com/micrari/p/7612962.html,如有侵权,请联系作者。代码分析:
从TransactionInterceptor入手:
图4 invoke方法
TransactionInteceptor的invoke方法是对业务方法的一个拦截,然后把具体的拦截逻辑交给TransactionAspectSupport的invokeWithinTransaction进行处理。
TransactionAspectSupport.invokeWithinTransaction
图5 invokeWithinTransaction
方法invokeWithinTransaction规定了spring-tx事务处理的框架。首先获取当前事务,然后执行方法,执行出现异常之后进行回滚,清理资源,最后提交事务。
createTransactionIfNecessary
该方法会根据当前的事务属性来决定是否创建一个新的事务。
图6 createTransactionIfNecessary
该方法通过
PlatformTransactionManager.getTransaction()方法来获取当前事务,该方法会根据事务的传播属性来决定是否获取事务。下面是该方法的具体实现。AbstractPlatformTransactionManager#getTransaction
@Overridepublic final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException{
Object transaction = doGetTransaction();
// Cache debug flag to avoid repeated checks. booleandebugEnabled = logger.isDebugEnabled();
if (definition == null) {
// Use defaults if no transaction definition given. definition = newDefaultTransactionDefinition();
}
if(isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave. returnhandleExistingTransaction(definition, transaction, debugEnabled);
}
// Check definition settings for new transaction. 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. if(definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw newIllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation mandatory");
}
else if(definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
SuspendedResourcesHolder suspendedResources = suspend(null);
if(debugEnabled) {
logger.debug("Creating new transaction with name [" + definition.getName() + "]: "+ definition);
}
try{
booleannewSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
returnstatus;
}
catch(RuntimeException ex) {
resume(null, suspendedResources);
throwex;
}
catch(Error err) {
resume(null, suspendedResources);
throwerr;
}
}
else{
// Create "empty" transaction: no actual transaction, but potentially synchronization. booleannewSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
}
}备忘录模式的使用
图7 TransactionInfo
TransactionInfo里面有一个oldTransactionInfo属性,该属性是用来保存方法执行前的事务信息的。例如:方法A调用方法B,B的事务传播属性是REQUIRED_NEW。执行方法A时,假设事务属性是TxInfoA,此时当前的事务属性就是TxInfoA。当执行到方法B时,会创建一个新的TxInfoB来作为当前的事务属性,并且TxInfoB中的oldTransactionInfo就是TxInfoA,当方法B执行完毕之后,会将oldTransactionInfo恢复为当前的事务属性。
ThreadLocal的使用
图8 TransactionSynchronizationManager
spring-tx中使用ThreadLocal来保存当前事务的资源,具体是放在
TransactionSynchronizationManager类中。具体包括当前的连接、事务名称、事务隔离级别、当前事务是否活跃以及事务同步回调。 3.事务传播属性REQUIRED
如果当前无事务则开启一个事务,否则加入当前事务。SUPPORTS
如果当前有事务则加入当前事务。MANDATORY
如果当前无事务则抛出异常,否则加入当前事务。REQUIRES_NEW
如果当前无事务则开启一个事务,否则挂起当前事务并开启新事务。NOT_SUPPORTED
如果当前有事务,则挂起当前事务以无事务状态执行方法。NEVER
如果当前有事务,则抛出异常。NESTED
创建一个嵌套事务,如果当前无事务则创建一个事务。图9 tx-propagation
为了方便记忆,我做了上面的图。可以看到REQUIRED、REQUIRED_NEW、NESTED都需要事务;SUPPORT、NOT_SUPPORT,则对事务是否存在要求不高;而MADTORY、NEVER分别代表了两个极端,一个是必须有事务,一个是必须没有事务。
所以可以有如下记法:最左边是需要以事务的方式执行,中间对事务的要求不高,而最右则比较极端。
REQUIRED、REQUIRED_NEW、NESTED <----- SUPPORTED、NOT_SUPPORTED --> MANDATORY、NEVER
4.XA事务XA是由X/Open组织提出的分布式事务的规范。XA规范主要定义了(全局)事务管理器(TM)和(局 部)资源管理器(RM)之间的接口。主流的关系型 数据库产品都是实现了XA接口的。
图10 XA事务
备注:图片来自
https://blog.csdn.net/wuzhiwei549/article/details/79925618XA事务主要就是2PC(2阶段提交)的一个实现,其中TM是全局的事务管理器,用来管理全局事务。RM是本地资源管理器,用来管理本地事务。应用程序首先要向TM中注册资源,然后才能对资源进行操作。
5.Mybatis是怎么和Spring-Tx交互的疑问:有没有想过这样一个问题,spring集成mybatis时,mybatis是怎么获取到当前的数据库连接的呢,带着这个疑问,我们来思考这个问题。
我们知道Mybatis的核心是SqlSessionFactory,用这个类来获取SqlSession对象,SqlSession对象可以理解成一个数据库连接,通过这个连接可以执行Sql,然后转换结果。
图11 DefaultSqlSessionFactory
这个方法通过
TransactionFacotry.newTransaction来获取当前事务。图12 TransactionFactory
如图12,TransactionFactory有三个子类,其中SpringManagedTransaction就是我们要招的类。
图13 SpringManagedTransaction
可以看到Mybatis是使用的Spring-Tx提供的DataSoureUtils类来获取当前的连接。
图14 DataSourceUtils
图14是
DataSourceUtils.doGetConnection方法,其实就是拿的TransctionSynchronizationManager.getResource方法,也就是获取当前线程的连接。所以总结起来就是,spring集成mybatis时,mybatis使用的是spring提供的DataSourceUtils类来获取当前线程的连接。
作者心得:应该说工作之后能力还是有提高的,在读研期间,这种源码根本就读不懂。但是工作后,也能够读懂这些框架的源码了。spring-tx也是看了有两周,过程中也是有不懂的地方,对于我来说,不懂的我会把它当成一个点,去”定点爆破“,会把这个问题记到脑子里面,会有一段时间都在思考这个点,去搜集资料,反复阅读源码。spring-tx这篇文章我总结了两个阅读源码的方法:
要站在更高的层次看源码,刚开始不要去纠结其具体的实现细节,首先把代码的整体架构梳理出来;看看这个代码具体是解决了哪些问题,提供了哪些能力。
要站在更高的层次看问题,要在宏观上看待问题,而不要去纠结于细节。
6.其它github主页:
https://github.com/youngFFgit仓库地址:
https://github.com/youngFF/MyHearthStone.gitgitbook地址:
https://youngff.github.io/MyHearthStone/欢迎各位加入鬣狗技术社区,希望能够为您提供有思考、有深度的文章。欢迎加入我们,如果你也有想法在鬣狗技术社区发表文章,头条私聊即可。
求 关注➕转发➕点赞,谢谢各位!您的支持就是我们更新的动力