spring-framework 版本:v5.3.19
为减少篇幅,本篇只研究最常用的注解声明式事务
AutoProxyRegistrar 没什么好说的,就只是引入一个 InfrastructureAdvisorAutoProxyCreator,这个AutoProxyCreator在spring启动时会去找role是ROLE_INFRASTRUCTURE的advisor,从而实现增强bean。
主要来看 ProxyTransactionManagementConfiguration 干了啥。
这个Configuration引入了 BeanFactoryTransactionAttributeSourceAdvisor (这正是刚刚所提到的role = ROLE_INFRASTRUCTURE的advisor)。而advisor所需要的 pointcut 和 advice 也同样在这个configuration引入了,分别是 AnnotationTransactionAttributeSource,TransactionInterceptor。
TransactionAttributeSource并不是 Pointcut 的子类,反而更像一个工具类,其作用是判断是否为需要事务代理的类以及解析相关的事务参数信息。以最常用的 AnnotationTransactionAttributeSource 为例,作用就是判断类或方法是否有@Transaction注解和解析该注解上的信息。
真正的pointcut由一个匿名内部类定义,只是这个匿名内部类的实现需要一个TransactionAttributeSource。
看到pointcut的 ClassFilter 和 MethodMatcher
其实poincut的底层匹配逻辑就在之前configuration引入的AnnotationTransactionAttributeSource。
TransactionAttributeSourcePointcut继承自StaticMethodMatcherPointcut继承自StaticMethodMatcher实现了MethodMatcher,所以TransactionAttributeSourcePointcut本身就是一个MethodMatcher。
接下来上硬菜,看到 advisor 的 advice (代理逻辑)—— TransactionInterceptor
invokeWithinTransaction 就是一个事务常用的 try{…}catch{…}finlly{…} 代码框架
@Nullable
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);
final TransactionManager tm = determineTransactionManager(txAttr);
//一般不会走这个分支(直接不看)
if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
// ...
}
//如果不是上面的类型,则事务管理器一定是这个类型
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 {
//恢复ThreadLocal到上一个transactionInfo
cleanupTransactionInfo(txInfo);
}
// 假如返回值是 Try.of(()->{...}) 这样的类型,则真正执行获取结果,若异常则设置rollbackOnly=true
if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
}
//提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
// 编程式事务
else {
// ...
}
}
TransactionStatus:当前事务状态
TransactionInfo:封装了当前事务状态和上一个事务信息,并与当前线程进行绑定
tm.getTransaction 结合传播级别获取当前方法的事务状态 TransactionStatus
@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException {
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
// 新建一个 txObject 维护了 ConnectionHolder(数据库连接)
Object transaction = doGetTransaction();
boolean debugEnabled = logger.isDebugEnabled();
//当前是否存在事务,其实就是检查txObject 也即 threadLocal中有没有 ConnectionHolder
if (isExistingTransaction(transaction)) {
//如果存在则走该分支
return handleExistingTransaction(def, transaction, debugEnabled);
}
//如果不存在走下面的分支
//检查超时时间配置的合法性
if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
}
// 当传播机制是PROPAGATION_MANDATORY,表示当前必须存在一个事务,否则抛出异常
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
//开启一个新的数据库事务
else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
//挂起现在的事务状态(因为当前没有事务,所以这里传null)
// 实际上第一次进入时当前线程中也不可能有事务状态所以什么都不会做,除非其他如外部代码手动设值
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
}
try {
//开启事务
return startTransaction(def, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
//剩下的传播机制创建空事务
// 空事务可以理解为 存在封装的spring事务即TransactionStatus对象但实际上mysql是非事务运行的
else {
if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + def);
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
// 封装一个 TransactionStatus 其中 transaction表示此刻的spring事务,newTransaction表示当前spring事务是这个方法新建的(后续会真正提交时会真正commit),
return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
}
}
主要看一些关键代码
这里组装事务对象的时候,会根据dataSource从threadLocal中获取数据库连接句柄,后续根据是否存在数据库连接从而判断当前是否存在事务。对应的代码是isExistingTransaction(transaction) 判断后走不同的分支代码。
其中开启一个事务的方法 startTransaction:dobegin 真正开启一个数据库事务并同步 ThreadLocal。
doBegin
附上一张传播机制图
代码注释中多次提到 ThreadLocal,所以存了啥?
大部分在 TransactionSynchronizationManager 类
除此之外,还有 TransactionAspectSupport 类里的 transactionInfoHolder 记录当前线程的 transactionInfo
其中挂起的方法 suspend :挂起当前事务,包括 TransactionStatus 和 threadLocal 中的值并返回一个挂起资源
总之,createTransactionIfNecessary 的主要逻辑就是根据当前的事务传播机制来创建一个 TransactionInfo。而 TransactionInfo 封装了当前事务状态 TransactionStatus 和上一个事务信息,并与当前线程进行绑定。需要注意的是虽然每次都会返回一个新的spring事务对象 TransactionInfo ,但是不一定每次都会真正创建数据库事务。
其实就是调用事务管理器的 commit 方法
值得注意的是,虽然调用了事务管理器的 commit 方法,但是如果被标记了 localRollbackOnly 或者 globalRollbackOnly 时仍然可能执行 processRollback 方法回滚。
localRollbackOnly 和 globalRollbackOnly 以及回滚的逻辑后面再做解释
这里先简单的记住这么一个逻辑:如果被标记了localRollbackOnly 或者 globalRollbackOnly 即使执行到了提交方法,也仍然可能回滚。
先不管回滚,看 processCommit 提交的逻辑
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
boolean unexpectedRollback = false;
prepareForCommit(status);
//调用回调器提交前
triggerBeforeCommit(status);
//调用回调器完成前
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
//savePoint的提交
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
unexpectedRollback = status.isGlobalRollbackOnly();
status.releaseHeldSavepoint();
}
//newTransaction为true表示当前这次执行的提交方法是真正需要提交的那次
//也就是说只有当当前spring事务是这个方法新建的才真正提交
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
//一般来说走到这里,GlobalRollbackOnly不可能为true
// 除非配置的事务管理器重写了 shouldCommitOnGlobalRollbackOnly 方法返回true,表示就算标记了全局回滚也提交
unexpectedRollback = status.isGlobalRollbackOnly();
//提交事务
doCommit(status);
}
//如果需要提前终止就可以在这里设置unexpectedRollback后续判断是否抛异常提前终止
else if (isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = status.isGlobalRollbackOnly();
}
//抛异常非预期回滚
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
catch (UnexpectedRollbackException ex) {
//回调器完成后
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
}
catch (TransactionException ex) {
if (isRollbackOnCommitFailure()) {
//提交异常就回滚
doRollbackOnCommitException(status, ex);
}
else {
//回调器完成后
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
}
catch (RuntimeException | Error ex) {
if (!beforeCompletionInvoked) {
//回调器完成前
triggerBeforeCompletion(status);
}
//提交异常就回滚
doRollbackOnCommitException(status, ex);
throw ex;
}
try {
//回调器提交后
triggerAfterCommit(status);
}
finally {
//回调器完成后
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
}
finally {
//恢复挂起事务
cleanupAfterCompletion(status);
}
}
提交过程中,只有当前spring事务是这个方法(这里的方法是指@Transaction注解的那些方法)新建的才会真正进行数据库提交。比如说简单一点就是:所执行的提交和当前事务的创建事务在同一个前面说到的 try{}catch{}finally{} 事务代码框架。
同时还可以发现有很多 triggerBeforexxx或者triggerAfterxxx 的方法,这些其实就是执行注册的同步器 TransactionSynchronization 的回调。而这些同步器可以同步事务管理器 TransactionSynchronizationManager.registerSynchronization 方法注册。
其实单纯的说这个方法为回滚事务是不准确的。因为跟3.2的 commitTransactionAfterReturning 可能回滚一样,这里的 completeTransactionAfterThrowing 也有可能提交。
可以发现回滚和提交一样,都是以事务管理器的api为入口的。而这里对应的api入口就是 rollback 方法,进而调用到 processRollback 。
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;
try {
triggerBeforeCompletion(status);
//有 savepoint 就回滚到指定位置(NESTED)
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
status.rollbackToHeldSavepoint();
}
//如果当前spring事务是该方法新建的就直接回滚
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
doRollback(status);
}
//如果当前执行方法是公用了一个已经存在的事务,则判断整个事务要不要回滚看具体配置
else {
if (status.hasTransaction()) {
// 若 rollbackOnly(特别标记要回滚 默认为false) 或者 globalRollbackOnParticipationFailure(部分失败全局回滚 默认为true)则标记为true
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
// 标记当前事务状态 globalRollbackOnly=true 表示需要回滚
// 这里只是标记不做真正回滚,真正的回滚是当后续提交的时候会判断这个标记,再进行回滚
doSetRollbackOnly(status);
}
else {
if (status.isDebug()) {
logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
}
}
}
else {
logger.debug("Should roll back transaction but cannot - no transaction available");
}
// 是否提前失败
if (!isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = false;
}
}
}
catch (RuntimeException | Error ex) {
// 回调 afterCompletion
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}
// 回调 afterCompletion
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
// 如果非预期,就直接抛异常中止程序
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
}
finally {
cleanupAfterCompletion(status);
}
}
回滚同样会进行 hasSavepoint 和 hasTransaction 的判断处理以及同步器的回调调用。除此之外,当不是真正需要回滚的事务方法时,即使发生异常了并不会立即执行数据库的回滚,只是标记了 globalRollbackOnly 为true,而这里的标记就和刚刚 commit 时没有讲到的rollback对应上了。
从代码注释中也可以看到除了 rollbackOnly 和 globalRollbackOnly 标记外还有其他类似的标记/方法。
rollbackOnly:标记当前事务回滚(这个标记是跟着单个spring事务的)
globalRollbackOnly:标记全局事务回滚(这个标记是跟着数据库连接的)
globalRollbackOnParticipationFailure:部分异常全局回滚(默认true)
shouldCommitOnGlobalRollbackOnly():当发生全局回滚时是否提交(默认false)
isFailEarlyOnGlobalRollbackOnly():当发生全局回滚时是否提前终止(默认 false)