我们在spring 事务源码赏析(一) 中分析了spring事务是如何找到目标方法,并如何将事务的逻辑织入到我们的业务逻辑中。本篇我们将会看到spring事务的核心实现:
1.事务传播机制的实现
2.事务隔离级别的实现
3.事务的回滚提交等操作的实现
好了,废话不多说,接着上一篇,我们讲到了TransactionInterceptor这个切面的逻辑,先看一下类图:
简单介绍一下这个类图:
Advice是一个切面的最顶层接口,是一个空接口,用于表示这是一个切面,integrceptor接口也是一个空接口,顾名思义,实现这个接口表示是拦截器,至于是什么样的拦截器呢,有方法拦截器,构造函数拦截器,属性拦截器,我们这里就是方法拦截器MethodInterceptor,
再看另外一个InitializingBean接口,这个是一个由容器调用的接口,当我们bean完成初始化后,容器会调用这个接口的afterPropertiesSet方法。
还有一个Aware接口,是所有带有aware接口的最顶层接口,同样是空接口,这里实现的是BeanFactoryAware,在BeanFactory完成组装的时候会调用setBeanFactory方法,将BeanFactory传递给调用方,具体的调用请看我的另一个系列:spring5之容器始末源码赏析 (一)总览。
最后TransactionAspectSupport是一个抽象模板类,抽象类事务拦截器的逻辑。
在正真读代码之前我们先明确几个重要的类:
PlatformTransactionManager:这是事务的管理类,事务的核心逻辑包括事务的获取,提交,回滚都在这。
TransactionStatus:事务的状态,这个类可以说是穿针引线,贯穿整个事务。
好了,我们开始。我们知道,事务的最小粒度是方法级别的,所以它实现了MethodInterceptor接口,在Pointcut切点匹配到具体的方法时(@Transactional注解所注释的方法),MethodInterceptor接口的invoke()的逻辑会被织入业务逻辑中,我们来看一下invoke的实现:
@Override @Nullable public Object invoke(final MethodInvocation invocation) throws Throwable { // Work out the target class: may be {@code null}. // The TransactionAttributeSource should be passed the target class // as well as the method, which may be from an interface. Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); // Adapt to TransactionAspectSupport's invokeWithinTransaction... return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed); }
我们看到通过处理,spring获取了目标类的Class对象,并传递给了invokeWithinTransaction方法,invocation::proceed是函数式编程的写法,实际上是InvocationCallback的一个实现,它会在代理逻辑执行完以后继续执行原有逻辑。
我们来看一下invokeWithinTransaction方法:
@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 PlatformTransactionManager tm = determineTransactionManager(txAttr); final String joinpointIdentification = methodIdentification(method, targetClass, txAttr); if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) { // Standard transaction demarcation with getTransaction and commit/rollback calls. TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); Object retVal = null; 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); } commitTransactionAfterReturning(txInfo); return retVal; } else { ...... } }
这里我们只看一半,为什么,第一因为我们平时默认经常使用的TransactionManager是DataSourceTransactionManager(后面会介绍),它并没有实现CallbackPreferringPlatformTransactionManager,所以我们经常使用的其实只有前一半的逻辑,
第二后一半的事务处理与前一半逻辑基本一样,区别在后者将事务的逻辑放到一个内部类中交给第三方管理可以看一下:WebSphereUowTransactionManager,这里不再详细叙述。
我们梳理一下整个流程,再一步步的看:
第一步,spring先获取了TransactionAttribute,这类的实例是存储了@Transactional注解的解析结果,这个在上一篇已经分析过了。
第二步,spring开始准备事务了,所谓准备事务,就是开启事务,下面我们会着重分析这里。
第三步,如果没有异常则会继续执行原有逻辑。实际上是继续执行拦截链的下一个节点。(这里注意了,如果一个方法被多个拦截器发现,那么会根据先后顺序组成一个链表一次执行,这里要注意一下事务拦截器实行的位置,有时候动态数据源要考虑这个问题)。
第四步,如果有异常,则根据规则处理异常,如果没有则跳过
第五步,事务的后置处理,将之前被挂起的事务设置回来(如果有的话)
第六步, 提交事务
我们来看一下spring如何准备事务:
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);
}
可以看到,贯穿整个事务的核心类就是TransactionStatus,这个类的实例记录了当前事务的运行状态,而这个实例的获取委托给了TransactionManager实例,我们看下DatasourceTransactionManager的抽象类AbstractPlatformTransactionManager的
实现:
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException { Object transaction = doGetTransaction(); // Cache debug flag to avoid repeated checks. boolean debugEnabled = logger.isDebugEnabled(); if (definition == null) { // Use defaults if no transaction definition given. definition = new DefaultTransactionDefinition(); } if (isExistingTransaction(transaction)) { // Existing transaction found -> check propagation behavior to find out how to behave. return handleExistingTransaction(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 new IllegalTransactionStateException( "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 { boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); doBegin(transaction, definition); prepareSynchronization(status, definition); return status; } catch (RuntimeException | Error ex) { resume(null, suspendedResources); throw ex; } } else { // Create "empty" transaction: no actual transaction, but potentially synchronization. if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) { logger.warn("Custom isolation level specified but no actual transaction initiated; " + "isolation level will effectively be ignored: " + definition); } boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null); } }
这里就是事务的传播机制的实现了,首先,spring会先获取当前的事务,并判断当前事务是否存在,如果存在则走存在的逻辑(这个比较复杂,后面详细介绍).如果不存在,则会新开启一个事务,获取当前事务和判断是否存在事务的逻辑在具体的子类中实现:
@Override protected Object doGetTransaction() { DataSourceTransactionObject txObject = new DataSourceTransactionObject(); txObject.setSavepointAllowed(isNestedTransactionAllowed()); ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource()); txObject.setConnectionHolder(conHolder, false); return txObject; }
红色的方法其实是从ThreadLocal中获取的,也就是说获取和当前线程绑定的数据库链接并赋值给DataSourceTransactionObject
@Override protected boolean isExistingTransaction(Object transaction) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive()); }
如果当前链接存在,并且是活跃状态的话,那么可以确定,之前已经有事务开启了,否则当前事务为一个新的事务。
我们来看一下如果当前事务是新的事务的话,spirng的处理过程:
如果传播行为是TransactionDefinition.PROPAGATION_MANDATORY的话,就会抛出异常,如果传播行为是PROPAGATION_REQUIRED或PROPAGATION_REQUIRES_NEW或PROPAGATION_NESTED的话就新建事务,可以看到,在之前没有事务的情况下,这三种传播行为的逻辑是一样的。注意,这里suspend一个null,目的是为了把当前线程的ThreadLocal中的值清空,suspend中的逻辑我们会在分析当前已存在事务的逻辑中展示。我们先看一下doBegin的逻辑:
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); // 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); } }
doBegin的逻辑很简单,通过datasource获取connection,设置autocommi为false,通过connection设置数据库的事务隔离级别,最后,如果是新的链接的话,会将connectionHolder与当前线程绑定。我们发现,事务的状态是放在transactionstatus对象中的,而事务的核心:数据库的Connection对象则是维护在ThreadLocal中,与当前线程绑定。
我们接着看prepareSynchronization方法:
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(); } }
TransactionSynchronizationManager里面是一堆ThreadLocal,这里是为了spring做数据同步用的,看看就可以了。
到此,事务算是真正开启了,不过,这只是当前事务不存在的情况下的逻辑,我们来看一下若当前事务已经存在了,spring的处理逻辑:
private TransactionStatus handleExistingTransaction( TransactionDefinition definition, Object transaction, boolean debugEnabled) throws TransactionException { if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) { throw new IllegalTransactionStateException( "Existing transaction found for transaction marked with propagation 'never'"); } if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) { if (debugEnabled) { logger.debug("Suspending current transaction"); } Object suspendedResources = suspend(transaction); boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); return prepareTransactionStatus( definition, null, false, newSynchronization, debugEnabled, suspendedResources); } if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) { if (debugEnabled) { logger.debug("Suspending current transaction, creating new transaction with name [" + definition.getName() + "]"); } SuspendedResourcesHolder suspendedResources = suspend(transaction); try { boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); doBegin(transaction, definition); prepareSynchronization(status, definition); return status; } catch (RuntimeException | Error beginEx) { resumeAfterBeginException(transaction, suspendedResources, beginEx); throw beginEx; } } if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { if (!isNestedTransactionAllowed()) { throw new NestedTransactionNotSupportedException( "Transaction manager does not allow nested transactions by default - " + "specify 'nestedTransactionAllowed' property with value 'true'"); } if (debugEnabled) { logger.debug("Creating nested transaction with name [" + definition.getName() + "]"); } if (useSavepointForNestedTransaction()) { // Create savepoint within existing Spring-managed transaction, // through the SavepointManager API implemented by TransactionStatus. // Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization. DefaultTransactionStatus status = prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null); status.createAndHoldSavepoint(); return status; } else { // Nested transaction through nested begin and commit/rollback calls. // Usually only for JTA: Spring synchronization might get activated here // in case of a pre-existing JTA transaction. boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, null); doBegin(transaction, definition); prepareSynchronization(status, definition); return status; } } // Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED. if (debugEnabled) { logger.debug("Participating in existing transaction"); } if (isValidateExistingTransaction()) { if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) { Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel(); if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) { Constants isoConstants = DefaultTransactionDefinition.constants; throw new IllegalTransactionStateException("Participating transaction with definition [" + definition + "] specifies isolation level which is incompatible with existing transaction: " + (currentIsolationLevel != null ? isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) : "(unknown)")); } } if (!definition.isReadOnly()) { if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) { throw new IllegalTransactionStateException("Participating transaction with definition [" + definition + "] is not marked as read-only but existing transaction is"); } } } boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null); }
首先,若传播行为是PROPAGATION_NEVER的话,直接抛出异常,若是PROPAGATION_NOT_SUPPORTED,则挂起当前事务,若是PROPAGATION_REQUIRES_NEW,则挂起当前事务,并开启新的事务,可以看到,开启新的事务的逻辑和上面的一模一样。
若是PROPAGATION_NESTED,则记录一个savepoint,然后继续。所以关键的地方就是这个挂起的逻辑了,spring是如何挂起,又是在什么时候如何把挂起的事务继续的,我们接下来揭晓:
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException { if (TransactionSynchronizationManager.isSynchronizationActive()) { ...... } 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; } }
核心就两行,我把不需要关注的删掉了,那个是spring同步数据的,继续看:
@Override protected Object doSuspend(Object transaction) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; txObject.setConnectionHolder(null); return TransactionSynchronizationManager.unbindResource(obtainDataSource()); }
还记得之前执行doBegin方法的时候向TransactionSynchronizationManager中放入的是什么吗?没错,是ConnectionHolder,就是承载了数据库Connection对象的实例,这里将这个对象与当前线程解绑,并返回给SuspendedResourcesHolder,这个SuspendedResourcesHolder会被传递给TransactionStatus:
protected DefaultTransactionStatus newTransactionStatus( TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction, boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) { boolean actualNewSynchronization = newSynchronization && !TransactionSynchronizationManager.isSynchronizationActive(); return new DefaultTransactionStatus( transaction, newTransaction, actualNewSynchronization, definition.isReadOnly(), debug, suspendedResources); }
TranscationStatus不仅存有事务的状态,还有被挂起的事务的链接。到这里,spring根据配置,已经完成了事务的传播行为,和隔离级别的设置,接下来便是执行业务代码。
执行完业务代码会有两个结果,1.业务代码正常完成,需要提交事务2.业务代码发生异常,需要回滚事务。
我们先看第一种情况:
public final void commit(TransactionStatus status) throws TransactionException { if (status.isCompleted()) { throw new IllegalTransactionStateException( "Transaction is already completed - do not call commit or rollback more than once per transaction"); } DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; if (defStatus.isLocalRollbackOnly()) { if (defStatus.isDebug()) { logger.debug("Transactional code has requested rollback"); } processRollback(defStatus, false); return; } if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) { if (defStatus.isDebug()) { logger.debug("Global transaction is marked as rollback-only but transactional code requested commit"); } processRollback(defStatus, true); return; } processCommit(defStatus); }
在判断是否需要roolback后,便处理提交sql的逻辑:
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); } }
可以看到,如果当前有savePoint则释放掉savepoint,如果当前是新的事务,则提交事务:
protected void doCommit(DefaultTransactionStatus status) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); Connection con = txObject.getConnectionHolder().getConnection(); if (status.isDebug()) { logger.debug("Committing JDBC transaction on Connection [" + con + "]"); } try { con.commit(); } catch (SQLException ex) { throw new TransactionSystemException("Could not commit JDBC transaction", ex); } }
后面的cleanupAfterCompletion方法中会释放链接,这里就不详细介绍了,至此,我们一个事务的整个生命周期已经结束,下面要做的,就是把transactionstatus中存放的suspendsource释放出来(如果有的话),与当前线程绑定,让被挂起的事务重新提上日程。
好了spring事务源码的分析就告一段落了,因个人水平有限,有任何不足或者理解错误的地方希望大家谅解并指正。谢谢~
转载请注明出处