书接上回
通过上一篇以及之前的几篇,已经吧Spring Aop的识别,解析,加载,组装,以及bean的动态代理的过程都讲解了。所以本文会直接通过讲解spring事物的拦截器的执行逻辑讲解 @Transactional 执行过程
一般情况下,springboot已经默认使用CGlib的方式创建动态代理了,所以本文会切换到lCgib动态代理的执行过程,但是其实抛去使用的库的差异,CGlib和JDK创建代理以及代理逻辑执行其实没有太大的差别
CglibAopProxy 创建的代理,会在 org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept 方法中创建一个 CglibMethodInvocation (MethodInvocation类型) , 而它其实继承 ReflectiveMethodInvocation ,接着还是调用其proceed方法
TransactionInterceptor
这个就是Spring内置的事务拦截器的实现,实现了MethodInterceptor接口,那就直接看其 invoke 实现吧
把涉及到的两个方法拿出来,可以很明显的看到这个拦截器就是一个环绕逻辑,其主要也就做了下面几件事
1、解析方法上的事务注解,然后封装成 TransactionAttribute
2、获取事务管理器 TransactionManager
3、创建 TransactionInfo
4、执行 methodInvocation.proceed 返回控制权或者说执行真正的业务方法逻辑
5、cleanupTransactionInfo(txInfo); 清理TransactionInfo
6、commitTransactionAfterReturning(txInfo); 提交事务并执行一些callback逻辑
下面详细详解下以上6步
@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
@Override
@Nullable
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
@Override
public Object getTarget() {
return invocation.getThis();
}
@Override
public Object[] getArguments() {
return invocation.getArguments();
}
});
}
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
final TransactionManager tm = determineTransactionManager(txAttr);
//.......省略......
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
Object result;
final ThrowableHolder throwableHolder = new ThrowableHolder();
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> {
TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
try {
Object retVal = invocation.proceedWithInvocation();
if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
return retVal;
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new ThrowableHolderException(ex);
}
}
else {
// A normal return value: will lead to a commit.
throwableHolder.throwable = ex;
return null;
}
}
finally {
cleanupTransactionInfo(txInfo);
}
});
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
catch (TransactionSystemException ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
ex2.initApplicationException(throwableHolder.throwable);
}
throw ex2;
}
catch (Throwable ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
}
throw ex2;
}
// Check result state: It might indicate a Throwable to rethrow.
if (throwableHolder.throwable != null) {
throw throwableHolder.throwable;
}
return result;
}
}
1、解析TransactionAttribute
这个解析工作其实在前面介绍事务Advisor的时候有提到过正是利用了配置注入中set的TransactionAttributeSource进行解析的。下面在看下,正是通过SpringTransactionAnnotationParser 对方法上的 @Transactional 注解进行解析,然后创建了一个RuleBasedTransactionAttribute
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
Propagation propagation = attributes.getEnum("propagation");
rbta.setPropagationBehavior(propagation.value());
Isolation isolation = attributes.getEnum("isolation");
rbta.setIsolationLevel(isolation.value());
rbta.setTimeout(attributes.getNumber("timeout").intValue());
String timeoutString = attributes.getString("timeoutString");
Assert.isTrue(!StringUtils.hasText(timeoutString) || rbta.getTimeout() < 0,
"Specify 'timeout' or 'timeoutString', not both");
rbta.setTimeoutString(timeoutString);
rbta.setReadOnly(attributes.getBoolean("readOnly"));
rbta.setQualifier(attributes.getString("value"));
rbta.setLabels(Arrays.asList(attributes.getStringArray("label")));
List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
rollbackRules.add(new RollbackRuleAttribute(rbRule));
}
for (String rbRule : attributes.getStringArray("rollbackForClassName")) {
rollbackRules.add(new RollbackRuleAttribute(rbRule));
}
for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {
rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
}
for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {
rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
}
rbta.setRollbackRules(rollbackRules);
return rbta;
}
2、获取事务管理器
获取事务管理器是通过
final TransactionManager tm = determineTransactionManager(txAttr); 这里执行的,其实最终还是通过 Spring的Bean容器获取TransactionManager类型的Bean
defaultTransactionManager = this.beanFactory.getBean(TransactionManager.class);
3、创建TransactionInfo
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification) 这一行代码真是内有千秋,有点复杂,也非常重要,做了很多事情,为了好理解,这里以第一次进入事务的场景为例进行讲解
可以看到逻辑被分成了两大步,
1、根据事务属性TransactionAttribute获取一个TransactionStatus
2、根据事务管理器,事务属性,前一步生成的事务状态,以及事物名称,生成一个TransactionInfo
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
// If no name specified, apply method identification as transaction name.
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
status = tm.getTransaction(txAttr);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
"] because no transaction manager has been configured");
}
}
}
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
这里有必要简单解释下这些实体的含义,
tm很好理解,他就是管理器,一个中心调度器,用于管理事务的,包括事务当前运行状态,事务提交与回滚的执行等
TransactionAttribute 本质是一个 TransactionDefinition ,就是事务的元信息
TransactionStatus 事务状态呢,相对TransactionDefinition表达的是一种动态的,运行期的对象,描述了当前事务的一些状态,包括会组合当前事务的一些资源信息(比如db的connection),以及还会存储挂起的资源信息(比如前一个被挂起的事务的db的connection)
TransactionInfo 这个嘛,既然已经有了TransactionStatus了,似乎已经够了吧,这个TransactionInfo 又是什么,为什么还要抽象一个它出来呢?那就要看下这个TransactionInfo的定义了。可以看到其实它是一个更大的容器,组合了tm,ta,ts,以及一个很重要的oldTransactionInfo。所以它和TransactionStatus还是有很大差异的,TransactionInfo记录一个事务所有的环境信息
protected static final class TransactionInfo {
@Nullable
private final PlatformTransactionManager transactionManager;
@Nullable
private final TransactionAttribute transactionAttribute;
private final String joinpointIdentification;
@Nullable
private TransactionStatus transactionStatus;
@Nullable
private TransactionInfo oldTransactionInfo;
public TransactionInfo(@Nullable PlatformTransactionManager transactionManager,
@Nullable TransactionAttribute transactionAttribute, String joinpointIdentification) {
this.transactionManager = transactionManager;
this.transactionAttribute = transactionAttribute;
this.joinpointIdentification = joinpointIdentification;
}
}
解释完这些领域实体的概念后,拆解上面两步,
status = tm.getTransaction(txAttr)
可以看到其实逻辑还是比较清晰的,先获取一个transaction对象() , 之后判断当前是否存在事务,如果存在,则通过handleExistingTransaction来处理(后面再讲解); 如果不存在(一般第一次执行事务都是走这条分支),则再根据事务传播属性分别走不同的逻辑,那一般情况,我们的事务方法都是使用默认的传播类型也就是required。所以会走到else if分支中
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();
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
return handleExistingTransaction(def, transaction, debugEnabled);
}
// Check definition settings for new transaction.
if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
}
// No existing transaction found -> check propagation behavior to find out how to proceed.
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) {
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;
}
}
else {
// Create "empty" transaction: no actual transaction, but potentially synchronization.
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);
return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
}
}
先看获取transaction 对象的过程
这里就是创建一个DataSourceTransactionObject对象,然后通过ThreadLocal的方式获取key=datasource的value ,也就是ConnectionHolder ,此时由于是第一次,所以这里是null
@Override
protected Object doGetTransaction() {
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
txObject.setSavepointAllowed(isNestedTransactionAllowed());
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
之后
else if(def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ) 逻辑
可以看到,其实逻辑也很简单,因为走到这里已经确定此时上下文中是么有事务的,所以这里直接挂起一个null,然后得到的挂起的资源,那肯定也是null了。之后就是开启一个新事务,然后封装成一个 TransactionStatus,并返回了
else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 由于这里是没有事务,所有会挂起挂起一个null,背后什么都没做,并且返回一个null
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
}
try {
//然后会开启一个新的事务并封装成一个TransactionStatus返回
return startTransaction(def, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
suspend(null)
这里如上所说,这里什么都不做,返回null
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
try {
Object suspendedResources = null;
if (transaction != null) {
suspendedResources = doSuspend(transaction);
}
String name = TransactionSynchronizationManager.getCurrentTransactionName();
TransactionSynchronizationManager.setCurrentTransactionName(null);
boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
TransactionSynchronizationManager.setActualTransactionActive(false);
return new SuspendedResourcesHolder(
suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
}
catch (RuntimeException | Error ex) {
// doSuspend failed - original transaction is still active...
doResumeSynchronization(suspendedSynchronizations);
throw ex;
}
}
else if (transaction != null) {
// Transaction active but no synchronization active.
Object suspendedResources = doSuspend(transaction);
return new SuspendedResourcesHolder(suspendedResources);
}
else {
// Neither transaction nor synchronization active.
return null;
}
}
startTransaction(def, transaction, debugEnabled, suspendedResources)
这个是核心方法,做了三件事
1、创建一个DefaultTransactionStatus
2、开启一个事务
3、为DefaultTransactionStatus 或者是事务上下文准备同步器
分别来分析
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
1、newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources) 这里很简单,就是根据传入的参数,通过构造器构造一个DefaultTransactionStatus 对象
2、doBegin(transaction, definition)
这个方法其实主要就做了两件事,查单当前 DataSourceTransactionObject中是否持有ConnectionHolder, 由上面可知,支持创建了 DataSourceTransactionObject 对象,并且由于是第一次进来,所以这里DataSourceTransactionObject并没有持有ConnectionHolder。所以这里会从datasource中获取connection,然后把connection生成一个ConnectionHolder并设置到DataSourceTransactionObject 中。
之后就是设置DataSourceTransactionObject的一些属性了,比较重要的一步是con.setAutoCommit(false)
最后一步很重要就是 TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder()); 把<datasource,connectionholder> 绑定到ThreadLocal中,之后就可以供诸如mybatis等框架可以从ThreadLocal中使用同一个datasource进行获取connection了
之前问题解答
所以从这里就可以解答之前提出的问题,spring在事务一开始的时候就创建了db connection, 而不管你后续有没有真正的db操作,或者如果遇到if提前返回的情况
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
txObject.setReadOnly(definition.isReadOnly());
// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
prepareTransactionalConnection(con, definition);
txObject.getConnectionHolder().setTransactionActive(true);
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// Bind the connection holder to the thread.
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, obtainDataSource());
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
3、prepareSynchronization
这个方法就是设置各种状态以及对象到ThreadLocal中,以及初始化事务同步器集合为一个空集合,供后续的使用
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
definition.getIsolationLevel() : null);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
TransactionSynchronizationManager.initSynchronization();
}
}
完成上面三步之后,就返回了一个装配好的 TransactionStatus ,可以梳理下现在这个TransactionStatus中都有组合了哪些东西
包括:
DataSourceTransactionObject (里面持有一个ConnectionHolder)
newTransaction = true (表示主事务)
newSynchronization = true (表示已经一个新的事务同步,由这个事务来控制 Synchronization的各种回调处理)
completed = false (事务尚未完成)
suspendedResources = null (没有挂起的资源)
接着
prepareTransactionInfo(tm, txAttr, joinpointIdentification, status)
这一步,主要是通过 上面创建好的TransactionStatus 创建一个 TransactionInfo
可以看到就是先通过传入的参数构建好一个TransactionInfo ,之后再把TransactionStatus 设置进去,最后把 TransactionInfo绑定到当前线程
protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, String joinpointIdentification,
@Nullable TransactionStatus status) {
TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
if (txAttr != null) {
//.........省略一些代码.........
txInfo.newTransactionStatus(status);
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No need to create transaction for [" + joinpointIdentification +
"]: This method is not transactional.");
}
}
txInfo.bindToThread();
return txInfo;
}
private void bindToThread() {
// Expose current TransactionStatus, preserving any existing TransactionStatus
// for restoration after this transaction is complete.
this.oldTransactionInfo = transactionInfoHolder.get();
transactionInfoHolder.set(this);
}
4、执行 retVal = invocation.proceedWithInvocation()
这一步没什么说的,就是执行业务方法
5、cleanupTransactionInfo(txInfo);
还原ThreadLocal
protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) {
if (txInfo != null) {
txInfo.restoreThreadLocalStatus();
}
}
private void restoreThreadLocalStatus() {
// Use stack to restore old transaction TransactionInfo.
// Will be null if none was set.
transactionInfoHolder.set(this.oldTransactionInfo);
}
6、commitTransactionAfterReturning(txInfo);
这个就是对事务进行提交以及执行callback逻辑了,方法最终会执行到这里,可以看到又是执行了很多小的步骤。其中prepareForCommit是空实现。
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
boolean unexpectedRollback = false;
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
unexpectedRollback = status.isGlobalRollbackOnly();
status.releaseHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
unexpectedRollback = status.isGlobalRollbackOnly();
doCommit(status);
}
else if (isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = status.isGlobalRollbackOnly();
}
// Throw UnexpectedRollbackException if we have a global rollback-only
// marker but still didn't get a corresponding exception from commit.
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
catch (UnexpectedRollbackException ex) {
// can only be caused by doCommit
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
}
catch (TransactionException ex) {
// can only be caused by doCommit
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;
}
// Trigger afterCommit callbacks, with an exception thrown there
// propagated to callers but the transaction still considered as committed.
try {
triggerAfterCommit(status);
}
finally {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
}
finally {
cleanupAfterCompletion(status);
}
}
看下 triggerBeforeCommit(status) 内部实现
可以看到就是获取到所有的Synchronization,然后逐个执行其beforeCommit方法,这就是Spring的事务钩子方法,其中在之前分析mybatis-spring 的时候,其中就在SqlSessionTemplate的代理中获取SqlSession的时候有注册一个SqlSessionSynchronization, 这里就会回调到SqlSessionSynchronization的beforeCommit方法
protected final void triggerBeforeCommit(DefaultTransactionStatus status) {
if (status.isNewSynchronization()) {
TransactionSynchronizationUtils.triggerBeforeCommit(status.isReadOnly());
}
}
public static void triggerBeforeCommit(boolean readOnly) {
for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {
synchronization.beforeCommit(readOnly);
}
}
SqlSessionSynchronization的beforeCommit方法 ,就是调用了sqlSession的commit方法
@Override
public void beforeCommit(boolean readOnly) {
// Connection commit or rollback will be handled by ConnectionSynchronization or
// DataSourceTransactionManager.
// But, do cleanup the SqlSession / Executor, including flushing BATCH statements so
// they are actually executed.
// SpringManagedTransaction will no-op the commit over the jdbc connection
// TODO This updates 2nd level caches but the tx may be rolledback later on!
if (TransactionSynchronizationManager.isActualTransactionActive()) {
try {
LOGGER.debug(() -> "Transaction synchronization committing SqlSession [" + this.holder.getSqlSession() + "]");
this.holder.getSqlSession().commit();
} catch (PersistenceException p) {
if (this.holder.getPersistenceExceptionTranslator() != null) {
DataAccessException translated = this.holder.getPersistenceExceptionTranslator()
.translateExceptionIfPossible(p);
if (translated != null) {
throw translated;
}
}
throw p;
}
}
}
其他的钩子方法不在一一分析了,基本都是相同的套路
接着就是
doCommit了,这里就是通过Connection做真正的commit
再Commit完成之后,又做了两次callback: triggerAfterCommit 和 triggerAfterCompletion
其中triggerAfterCompletion 比较重要一些,看下其中的逻辑。
主要就是如果当前的事务如果是主事务,那么就清空ThreadLocal中所有的Synchronizations, 然后callback Synchronization的afterCompletion 方法
private void triggerAfterCompletion(DefaultTransactionStatus status, int completionStatus) {
if (status.isNewSynchronization()) {
List<TransactionSynchronization> synchronizations = TransactionSynchronizationManager.getSynchronizations();
TransactionSynchronizationManager.clearSynchronization();
if (!status.hasTransaction() || status.isNewTransaction()) {
// No transaction or new transaction for the current scope ->
// invoke the afterCompletion callbacks immediately
invokeAfterCompletion(synchronizations, completionStatus);
}
else if (!synchronizations.isEmpty()) {
// Existing transaction that we participate in, controlled outside
// the scope of this Spring transaction manager -> try to register
// an afterCompletion callback with the existing (JTA) transaction.
registerAfterCompletionWithExistingTransaction(status.getTransaction(), synchronizations);
}
}
}
最后
cleanupAfterCompletion(status);
可以看到把 TransactionStatus 设置为完成,主要三件事
1、清理各种ThreadLocal
2、在事务完成之后,将TransactionStatus持有的DataSourceTransactionObject 也就是Connection返回给Datasource, 释放连接
3、恢复之前的事务上下文
下面分别拆解
private void cleanupAfterCompletion(DefaultTransactionStatus status) {
status.setCompleted();
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.clear();
}
if (status.isNewTransaction()) {
doCleanupAfterCompletion(status.getTransaction());
}
if (status.getSuspendedResources() != null) {
if (status.isDebug()) {
logger.debug("Resuming suspended transaction after completion of inner transaction");
}
Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
}
}
1、清理各种ThreadLocal
public static void clear() {
synchronizations.remove();
currentTransactionName.remove();
currentTransactionReadOnly.remove();
currentTransactionIsolationLevel.remove();
actualTransactionActive.remove();
}
2、在事务完成之后,将TransactionStatus持有的DataSourceTransactionObject 也就是Connection返回给Datasource, 释放连接
可以看到基本即使解绑Datasource和ConnectionHolder ,恢复Connection的各种默认属性,并将Connection返回回链接池,之后清理ConnectionHolder
protected void doCleanupAfterCompletion(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// Remove the connection holder from the thread, if exposed.
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.unbindResource(obtainDataSource());
}
// Reset connection.
Connection con = txObject.getConnectionHolder().getConnection();
try {
if (txObject.isMustRestoreAutoCommit()) {
con.setAutoCommit(true);
}
DataSourceUtils.resetConnectionAfterTransaction(
con, txObject.getPreviousIsolationLevel(), txObject.isReadOnly());
}
catch (Throwable ex) {
logger.debug("Could not reset JDBC Connection after transaction", ex);
}
if (txObject.isNewConnectionHolder()) {
if (logger.isDebugEnabled()) {
logger.debug("Releasing JDBC Connection [" + con + "] after transaction");
}
DataSourceUtils.releaseConnection(con, this.dataSource);
}
txObject.getConnectionHolder().clear();
}
3、恢复之前的事务上下文
这个当第一次时,不会执行,当在一个已经存在事务上下文中时,其实做的也很清晰,就是把之前存储的资源重新set回去
protected final void resume(@Nullable Object transaction, @Nullable SuspendedResourcesHolder resourcesHolder)
throws TransactionException {
if (resourcesHolder != null) {
Object suspendedResources = resourcesHolder.suspendedResources;
if (suspendedResources != null) {
doResume(transaction, suspendedResources);
}
List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
if (suspendedSynchronizations != null) {
TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
doResumeSynchronization(suspendedSynchronizations);
}
}
}
7、当事务存在,且第二个事务是Required_New类型时
对于这种场景,其实主要的处理步骤就是之前说的在创建TransactionStatus时,已经存在事务时,走 handleExistingTransaction(def, transaction, debugEnabled) 的逻辑了
这个方法里其实做的事情也很清晰,还是先获取下当前线程中的
DataSourceTransactionObject ,这时候肯定是有值的
之后的逻辑处理大同小异,也是根据新的方法中事务传播属性来创建TransactionStatus ,比如新进的方法的事务传播类型是Required_New,那么肯定之前获取的DataSourceTransactionObject 肯定是要作为被挂起的资源对象被TransactionStatus 持有,而新创建的TransactionStatus肯定是要重新获取一个Connection. 之后创建TransactionInfo ,同样,TransactionInfo中也会组合一个之前的oldTransactionInfo .
在新方法执行完成之后,原先持有的前一个方法的资源,都要被重新设置回去,然后继续走上一个方法事务拦截器的后置逻辑,也就是上一篇说的,先进来的interceptor的后置逻辑会后执行
总结
本篇算是完成了对spring事务拦截器主要逻辑的梳理,同时回答了之前提出的问题。
spring在事务一开始就创建了db的connection,而不管业务方法中是否真正有使用connection。当然spring其实也知道这事,当然也有对应的解决方案。不过,一般情况下,也不会出现业务方法都没操作db,方法确被添加了事务注解吧。但是可能出现,if提前返回,执行不到db的情况。此时创建的connection或者从连接池中获取的connection其实并没有被真正使用。
总的来说,由于spring考虑的非常全面而且覆盖的场景非常多,所以事务拦截器的实现逻辑还是比较复杂的,其中有很多组件的参与,以及其中有几个组件有大量的状态字段,spring通过这些状态控制了很多逻辑,导致整体逻辑如果想真正掌握还是有些难度的,但是如果只是理解整体的链路的话,相对来说不是很难