sprinboot中编程式事务_SpringBoot源码之旅——事务

前提本文代码基于SpringBoot的2.1.1.RELEASE版本。

spring事务是基于spring aop,而spring aop是基于spring ioc,所以在学习spring事务之前,要先了解bin17:SpringBoot源码之旅——IoC容器和bin17:SpringBoot源码之旅——AOP。

概述

概括来讲,事务是一个由有限操作集合组成的逻辑单元。事务操作包含两个目的,数据一致以及操作隔离。数据一致是指事务提交时保证事务内的所有操作都成功完成,并且更改永久生效;事务回滚时,保证能够恢复到事务执行之前的状态。操作隔离则是指多个同时执行的事务之间应该相互独立,互不影响。

事务是一个比较广泛的概念,事务管理资源除了我们熟知的数据库外,还可以包含消息队列、文件系统等。当然,一般来说,我们说的事务单指“数据库事务”。接下来我们会以MySQL数据库、Spring声明式事务为主要研究对象,但是很多事务概念、接口抽象和实现方式同时适用于其他情况。

准备

1,ACID,隔离级别,传播机制可能是最漂亮的Spring事务管理详解 - 掘金​juejin.im

2,AOP

声明式事务的实现就是通过AOP环绕增强的方式,在目标方法执行之前开启事务,在目标方法执行之后提交或者回滚事务。

TransactionInterceptor

对于基于JDK接口动态代理的AOP事务增强来说,由于接口的方法都必须是public的,这就要求实现类的实现方法也必须是public的(不能是protected、private等),同时不能使用static修饰符。所以,可以实施接口动态代理的方法只能是public或public final修饰符的方法,其他方法不能被动态代理,相应地也就不能实施AOP增强。

基于CGLib字节码动态代理的方案是通过扩展被增强类,动态创建其子类的方式进行AOP增强植入的。由于使用final、static、private修饰符的方法都不能被子类覆盖,相应的这些方法将无法实施AOP增强。

这些不能被spring事务增强的特殊方法并非就不工作在事务环境中,由于spring事务管理的传播级别,这些特殊方法和可被spring事务增强的方法的唯一区别在于是否可以主动启动一个新事务,如果这些特殊方法被无事务上下文的方法调用,则它们就工作在无事务上下文中,反之,如果被具有事务上下文的方法调用,则它们就工作在事务上下文中。

3,事务抽象

TransactionDefinition:用于描述事务的隔离级别、超时时间、是否为只读事务和事务传播规则等控制事务具体行为的事务属性。

TransactionStatus:代表一个事务的具体运行状态。事务管理器可以通过该接口获取事务运行期的状态信息,也可以通过该接口间接地回滚事务,它相比于在抛出异常时回滚事务的方式更具可控性。

PlatformTransactionManager:PlatformTransactionManager根据TransactionDefinition提供的事务属性配置信息创建事务,并用TransactionStatus描述这个激活事务的状态。

4,ThreadLocal

通过ThreadLocal,多个操作在同一个线程中共享数据库connection,不同线程互不影响,解决了线程安全问题。

在相同线程中进行相互嵌套调用的事务方法工作在相同的事务中。如果这些相互嵌套调用的方法工作在不同的线程中,则不同线程下的事务方法工作在独立的事务中。

GO!

AOP切面织入生成代理对象的过程,当Bean方法通过代理对象调用时,会触发对应的AOP增强拦截器,声明式事务是一种环绕增强,对应接口为MethodInterceptor,事务增强对该接口的实现为TransactionInterceptor

事务拦截器TransactionInterceptor在org.springframework.transaction.interceptor.TransactionInterceptor#invoke方法中,

public Object invoke(MethodInvocation invocation) throws Throwable {

Class> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;

Method var10001 = invocation.getMethod();

invocation.getClass();

return this.invokeWithinTransaction(var10001, targetClass, invocation::proceed);

}

通过调用父类TransactionAspectSupport的org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction方法进行事务处理,该方法支持声明式事务和编程式事务

protected Object invokeWithinTransaction(Method method, @Nullable Class> targetClass, TransactionAspectSupport.InvocationCallback invocation) throws Throwable {

// 查询目标方法事务属性、确定事务管理器、构造连接点标识(用于确认事务名称)

TransactionAttributeSource tas = this.getTransactionAttributeSource();

TransactionAttribute txAttr = tas != null ? tas.getTransactionAttribute(method, targetClass) : null;

PlatformTransactionManager tm = this.determineTransactionManager(txAttr);

String joinpointIdentification = this.methodIdentification(method, targetClass, txAttr);

Object result;

if (txAttr != null && tm instanceof CallbackPreferringPlatformTransactionManager) {

TransactionAspectSupport.ThrowableHolder throwableHolder = new TransactionAspectSupport.ThrowableHolder();

try {

result = ((CallbackPreferringPlatformTransactionManager)tm).execute(txAttr, (status) -> {

TransactionAspectSupport.TransactionInfo txInfo = this.prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);

Object var9;

try {

Object var8 = invocation.proceedWithInvocation();

return var8;

} catch (Throwable var13) {

if (txAttr.rollbackOn(var13)) {

if (var13 instanceof RuntimeException) {

throw (RuntimeException)var13;

}

throw new TransactionAspectSupport.ThrowableHolderException(var13);

}

throwableHolder.throwable = var13;

var9 = null;

} finally {

this.cleanupTransactionInfo(txInfo);

}

return var9;

});

if (throwableHolder.throwable != null) {

throw throwableHolder.throwable;

} else {

return result;

}

} catch (TransactionAspectSupport.ThrowableHolderException var19) {

throw var19.getCause();

} catch (TransactionSystemException var20) {

if (throwableHolder.throwable != null) {

this.logger.error("Application exception overridden by commit exception", throwableHolder.throwable);

var20.initApplicationException(throwableHolder.throwable);

}

throw var20;

} catch (Throwable var21) {

if (throwableHolder.throwable != null) {

this.logger.error("Application exception overridden by commit exception", throwableHolder.throwable);

}

throw var21;

}

} else {

// 事务获取

TransactionAspectSupport.TransactionInfo txInfo = this.createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

result = null;

try {

// 通过回调执行目标方法

result = invocation.proceedWithInvocation();

} catch (Throwable var17) {

// 目标方法执行抛出异常,根据异常类型执行事务提交或者回滚操作

this.completeTransactionAfterThrowing(txInfo, var17);

throw var17;

} finally {

// 清理当前线程事务信息

this.cleanupTransactionInfo(txInfo);

}

// 标方法执行成功,提交事务

this.commitTransactionAfterReturning(txInfo);

return result;

}

}

在讲Spring事务抽象时,有提到事务抽象的核心接口为PlatformTransactionManager,它负责管理事务行为,包括事务的获取、提交和回滚。在invokeWithinTransaction方法中,我们可以看到createTransactionIfNecessary、commitTransactionAfterReturning和completeTransactionAfterThrowing都是针对该接口编程,并不依赖于特定事务管理器,这里是对Spring事务抽象的实现。

提到事务传播机制时,我们经常提到一个条件“如果当前已有事务”,那么Spring是如何知道当前是否已经开启了事务呢?在AbstractPlatformTransactionManager中是这样做的

org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction

public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {

Object transaction = this.doGetTransaction();

boolean debugEnabled = this.logger.isDebugEnabled();

if (definition == null) {

definition = new DefaultTransactionDefinition();

}

if (this.isExistingTransaction(transaction)) {

return this.handleExistingTransaction((TransactionDefinition)definition, transaction, debugEnabled);

} else if (((TransactionDefinition)definition).getTimeout() < -1) {

throw new InvalidTimeoutException("Invalid transaction timeout", ((TransactionDefinition)definition).getTimeout());

...

}

注意getTransaction方法是final的,无法被子类覆盖,保证了获取事务流程的一致和稳定。抽象方法doGetTransaction获取当前事务对象,方法isExistingTransaction判断当前事务对象是否存在活跃事务,具体逻辑由特定事务管理器实现,看下我们使用最多的DataSourceTransactionManager对应的实现

org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction

protected Object doGetTransaction() {

DataSourceTransactionManager.DataSourceTransactionObject txObject = new DataSourceTransactionManager.DataSourceTransactionObject();

txObject.setSavepointAllowed(this.isNestedTransactionAllowed());

ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(this.obtainDataSource());

txObject.setConnectionHolder(conHolder, false);

return txObject;

}

org.springframework.transaction.support.TransactionSynchronizationManager#getResource

public static Object getResource(Object key) {

Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);

Object value = doGetResource(actualKey);

if (value != null && logger.isTraceEnabled()) {

logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");

}

return value;

}

org.springframework.transaction.support.TransactionSynchronizationManager#doGetResource

private static Object doGetResource(Object actualKey) {

Map map = (Map)resources.get();

if (map == null) {

return null;

} else {

Object value = map.get(actualKey);

if (value instanceof ResourceHolder && ((ResourceHolder)value).isVoid()) {

map.remove(actualKey);

if (map.isEmpty()) {

resources.remove();

}

value = null;

}

return value;

}

}

TransactionSynchronizationManager

TransactionSynchronizationManager通过ThreadLocal对象在当前线程记录了resources和synchronizations属性。resources是一个HashMap,用于记录当前参与事务的事务资源,方便进行事务同步,在DataSourceTransactionManager的例子中就是以dataSource作为key,保存了数据库连接,这样在同一个线程中,不同的方法调用就可以通过dataSource获取相同的数据库连接,从而保证所有操作在一个事务中进行。

synchronizations属性是一个TransactionSynchronization对象的集合,AbstractPlatformTransactionManager类中定义了事务操作各个阶段的调用流程,以事务提交为例

org.springframework.transaction.support.AbstractPlatformTransactionManager#processCommit

private void processCommit(DefaultTransactionStatus status) throws TransactionException {

try {

boolean beforeCompletionInvoked = false;

try {

boolean unexpectedRollback = false;

this.prepareForCommit(status);

this.triggerBeforeCommit(status);

this.triggerBeforeCompletion(status);

beforeCompletionInvoked = true;

...

}

我们可以看到,有很多trigger前缀的方法,这些方法用于在事务操作的各个阶段触发回调,从而可以精确控制在事务执行的不同阶段所要执行的操作,这些回调实际上都通过TransactionSynchronizationUtils来实现,它会遍历TransactionSynchronizationManager#synchronizations集合中的TransactionSynchronization对象,然后分别触发集合中各个元素对应方法的调用。

TransactionSynchronization

最后,流程图

demohttps://github.com/BinGithub2015/zhihu/tree/master/demo​github.com

参考

《精通Spring 4.x 企业应用开发实战》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值