问题起因
Spring 的 JDBC
相关的依赖库已经提供了对 JDBC
类事务处理的统一解决方案,在正常情况下,我们只需要在需要添加事务的业务处理方法上加上 @Transactional
注解即可开启声明式的事务处理。这种方式在单线程的处理模式下都是可行的,这是因为 Spring 在对 @Transactional
注解的切面处理上通过一些 ThreaLocal
变量来绑定了事务的相关信息,因此在单线程的模式下能够很好地工作。
然而,由于与数据库的交互属于 IO 密集型的操作,在某些情况下,与数据库的交互次数可能会成为性能的瓶颈。在这种情况下,一种可行的优化方式便是通过多线程的方式来并行化与数据库的交互,因为在大部分的情况下,数据的查询之间属于相互独立的任务,因此使用多线程的方式来解决这一类性能问题在概念上来讲是可行的
如果通过多线程的方式来优化,随之而来的一个问题就是对于事务的处理,可能客户端希望在一个任务出现异常时就回滚整个事务,就像使用 @Transactional
注解的效果一样。但是很遗憾,在这种情况下,我们不能直接使用 @Transactional
来开启事务,因为多线程的处理导致预先绑定的事务信息无法被找到,因此需要寻找其它的解决方案
Spring 事务源码分析
本文将仅分析声明式的事务处理方式,同时不会分析有关切面的具体加载逻辑,因为我们的目标是通过分析源码来找到在多线程下事务的可行解决方案
@Transaction
对应的切面处理是在 org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
方法中处理的,由于大部分情况下我们使用的都是声明式的阻塞式事务处理形式,因此我们也只关心这部分的处理逻辑:
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
/*
获取事务的相关属性,如需要回滚的异常,事务的传播方式等
*/
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
/*
根据当前所处的环境,决定具体要采用何种事务管理对象类型,对于 JDBC 事务来说,
该类型为 DataSourceTransactionManager
*/
final TransactionManager tm = determineTransactionManager(txAttr);
// 省略响应式事务的处理逻辑。。。。。。。
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// 检查是否需要开启事务,这里是我们重点分析的一个地方
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// 实际业务方法的处理
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 事务的回滚处理
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
// 省略部分代码。。。。。
// 提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
// 省略编程式的事务处理逻辑 。。。。。
}
接下来我们具体分析一下对于创建事务的有关处理:
protected TransactionInfo
createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr,
final String joinpointIdentification) {
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
// 实际创建事务的处理
status = tm.getTransaction(txAttr);
}
else {
// 省略部分代码
}
}
// 针对事务的准备工作,目的是为了将事务信息绑定到当前的线程
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
通过上面的分析,我们可以知道当前事务传里对象为 DataSourceTransactionManager
,我们继续查看它对创建事务的处理过程:
public final TransactionStatus
getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException {
// Use defaults if no transaction definition given.
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
// 获取与当前线程绑定的事务信息
Object transaction = doGetTransaction();
boolean debugEnabled = logger.isDebugEnabled();
/*
检查当前执行过程中是否已经存在事务,如果存在,那么需要通过对 TransactionDefinition 的 propagationBehavior
来对当前的执行做出合适的事务处理形式
*/
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
return hand