简单理解下其事务传播机制的体现以及生效原理。
1. mysql 和 jdbc 提供的保存点
在看事务源码的过程中发现有个savepoint 的一操作,简单研究下其使用。
1. mysql 提供的savepoint
start TRANSACTION; SELECT * FROM `message` insert into message(title) values ("tx1"); SAVEPOINT point_tx1; insert into message(title) values ("tx2"); SAVEPOINT point_tx3; insert into message(title) values ("tx3"); rollback to point_tx1; // 删除保存点(commit 之后也会删除回滚点) release SAVEPOINT point_tx1; // 同名会覆盖 SAVEPOINT point_tx1; // commit 也会删除点 commit;
2. JDBC提供的savepoint
import java.sql.*; public class Client { public static void main(String[] args) throws ClassNotFoundException, SQLException { try { Class.forName("com.mysql.jdbc.Driver"); Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/media?useSSL=false", "root", "123456"); con.setAutoCommit(false); Savepoint savepoint = null; PreparedStatement preparedStatement = con.prepareStatement("insert into message(title) values (\"tx1\")"); preparedStatement.execute(); savepoint = con.setSavepoint("point_tx1"); PreparedStatement preparedStatement2 = con.prepareStatement("insert into message(title) values (\"tx2\")"); preparedStatement2.execute(); // 回滚到指定保存点然后commit, 第二条数据会回滚 con.rollback(savepoint); con.commit(); } catch (Exception e) { e.printStackTrace(); } } }
2. 事务传播机制以及对dao 的影响
这里简单研究下事务AOP是如何影响到dao 的,也可以理解为如何传递connection 的; 接着研究下事务默认的传播行为。
1. 以mybatis 为例子分析其事务如何影响到dao
根据之前的操作大体可以理解到。事务相关实际是改变Connection 相关信息,也就是设置自动提交为false, 然后手动commit。 接下来简单研究下,事务在AOP层面修改为手动提交之后,dao 是如何拿到相对应的Connection 对象的。
1. 事务AOP修改Connection 对象相关属性
(1) org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction 标志事务AOP开始执行,调用createTransactionIfNecessary 获取一个TransactionInfo 对象, 这个对象包含事务管理器、事务属性、连接点、以及上一个事务信息, TransactionInfo 源码如下: (oldTransactionInfo 是一个重要的对象,可以理解为是执行权交给上一个事务的重要对象)
/** * Opaque object used to hold transaction information. Subclasses * must pass it back to methods on this class, but not see its internals. */ 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; } public PlatformTransactionManager getTransactionManager() { Assert.state(this.transactionManager != null, "No PlatformTransactionManager set"); return this.transactionManager; } @Nullable public TransactionAttribute getTransactionAttribute() { return this.transactionAttribute; } /** * Return a String representation of this joinpoint (usually a Method call) * for use in logging. */ public String getJoinpointIdentification() { return this.joinpointIdentification; } public void newTransactionStatus(@Nullable TransactionStatus status) { this.transactionStatus = status; } @Nullable public TransactionStatus getTransactionStatus() { return this.transactionStatus; } /** * Return whether a transaction was created by this aspect, * or whether we just have a placeholder to keep ThreadLocal stack integrity. */ public boolean hasTransaction() { return (this.transactionStatus != null); } private void bindToThread() { // Expose current TransactionStatus, preserving any existing TransactionStatus // for restoration after this transaction is complete. this.oldTransactionInfo = transactionInfoHolder.get(); transactionInfoHolder.set(this); } private void restoreThreadLocalStatus() { // Use stack to restore old transaction TransactionInfo. // Will be null if none was set. transactionInfoHolder.set(this.oldTransactionInfo); } @Override public String toString() { return (this.transactionAttribute != null ? this.transactionAttribute.toString() : "No transaction"); } }View Code
(2)调用org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction 获取一个TransactionStatus 事务状态对象, 这个方法会根据是否已经存在事务、事务传播行为等机制进行处理
/** * This implementation handles propagation behavior. Delegates to * {@code doGetTransaction}, {@code isExistingTransaction} * and {@code doBegin}. * @see #doGetTransaction * @see #isExistingTransaction * @see #doBegin */ @Override 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); } }View Code
1》org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction 获取DataSourceTransactionObject 对象,源码如下
protected Object doGetTransaction() { DataSourceTransactionObject txObject = new DataSourceTransactionObject(); txObject.setSavepointAllowed(isNestedTransactionAllowed()); ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource()); txObject.setConnectionHolder(conHolder, false); return txObject; }
这里会调用org.springframework.transaction.support.TransactionSynchronizationManager#getResource 获取ConnectionHolder 对象,这个对象可以理解为连接持有者。获取到之后设置到DataSourceTransactionObject 对象内部。
org.springframework.jdbc.datasource.DataSourceTransactionManager#obtainDataSource 获取到数据源之后作为参数传递下去。
protected DataSource obtainDataSource() { DataSource dataSource = getDataSource(); Assert.state(dataSource != null, "No DataSource set"); return dataSource; }
调用org.springframework.transaction.support.TransactionSynchronizationManager#getResource 获取ConnectionHolder (实际这里第一次获取到的对象为null)
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 从ThreadLocal 中获取
private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources"); private static Object doGetResource(Object actualKey) { Map<Object, Object> map = resources.get(); if (map == null) { return null; } Object value = map.get(actualKey); // Transparently remove ResourceHolder that was marked as void... if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) { map.remove(actualKey); // Remove entire ThreadLocal if empty... if (map.isEmpty()) { resources.remove(); } value = null; } return value; }
2》 调用org.springframework.jdbc.datasource.DataSourceTransactionManager#isExistingTransaction 判断是否存在事务环境,如果存在根据传播行为做特殊处理。这里第一次为false, 第一次开启事务则继续后续流程(也就是开启事务)
protected boolean isExistingTransaction(Object transaction) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive()); }
可以看到判断是否存在事务环境是判断ConnectionHolder 是否为空,以及其事务是否是激活状态。
3》 org.springframework.transaction.support.AbstractPlatformTransactionManager#startTransaction 开启事务
/** * Start a new transaction. */ 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; }
创建status 对象之后调用org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin 做开启事务操作
@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()); } }
这里实际就是操作Connection 的地方,调用数据源获取一个Connection对象,并且包到ConnectionHolder 之后设置到 txObject 对象中。然后修改为手动提交等设置调用 org.springframework.transaction.support.TransactionSynchronizationManager#bindResource 绑定到当前线程:(传递的key是dataSource, value是ConnectionHolder)
private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources"); public static void bindResource(Object key, Object value) throws IllegalStateException { Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); Assert.notNull(value, "Value must not be null"); Map<Object, Object> map = resources.get(); // set ThreadLocal Map if none found if (map == null) { map = new HashMap<>(); resources.set(map); } Object oldValue = map.put(actualKey, value); // Transparently suppress a ResourceHolder that was marked as void... if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) { oldValue = null; } if (oldValue != null) { throw new IllegalStateException("Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]"); } if (logger.isTraceEnabled()) { logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" + Thread.currentThread().getName() + "]"); } }
可以看到这里的逻辑是使用ThreadLocal 来进行线程间隔离,同时,local 中放的是个map 对象用于隔离多个对象。 最终实现的效果就是以dataSource 为key, 上面设置过的ConnectionHolder 为值放到map 中,并且线程间隔离。
(3) org.springframework.transaction.interceptor.TransactionAspectSupport#prepareTransactionInfo 用上面获取的status 对象然后去准备一个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) { // We need a transaction for this method... if (logger.isTraceEnabled()) { logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]"); } // The transaction manager will flag an error if an incompatible tx already exists. txInfo.newTransactionStatus(status); } else { // The TransactionInfo.hasTransaction() method will return false. We created it only // to preserve the integrity of the ThreadLocal stack maintained in this class. if (logger.isTraceEnabled()) { logger.trace("No need to create transaction for [" + joinpointIdentification + "]: This method is not transactional."); } } // We always bind the TransactionInfo to the thread, even if we didn't create // a new transaction here. This guarantees that the TransactionInfo stack // will be managed correctly even if no transaction was created by this aspect. txInfo.bindToThread(); return txInfo; }
第一步: 创建TransactionInfo 对象
第二步: org.springframework.transaction.interceptor.TransactionAspectSupport.TransactionInfo#bindToThread 绑定到当前线程。可以看到ThreadLocal 中放的永远是最新的TransactionInfo,当前TransactionInfo 内部记录了上个事务的状态, 可以理解为一个链式结构的用法。
private void bindToThread() { // Expose current TransactionStatus, preserving any existing TransactionStatus // for restoration after this transaction is complete. this.oldTransactionInfo = transactionInfoHolder.get(); transactionInfoHolder.set(this); }
org.springframework.transaction.interceptor.TransactionAspectSupport#transactionInfoHolder 是ThreadLocal
private static final ThreadLocal<TransactionInfo> transactionInfoHolder = new NamedThreadLocal<>("Current aspect-driven transaction");
2. Mybatis 使用上面修改的Connection 对象
mybatis 操作的时候最终都会交给对应Executor 的 doUpdate、 doQuery 方法。断点下到org.apache.ibatis.executor.SimpleExecutor#doUpdate 方法。
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null); stmt = prepareStatement(handler, ms.getStatementLog()); return handler.update(stmt); } finally { closeStatement(stmt); } } private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); stmt = handler.prepare(connection, transaction.getTimeout()); handler.parameterize(stmt); return stmt; }
下面追踪器getConnection 过程。
(1) org.apache.ibatis.executor.BaseExecutor#getConnection
protected Connection getConnection(Log statementLog) throws SQLException { Connection connection = transaction.getConnection(); // 如果开启debug, 创建代理对象打印日志, 否则直接返回 if (statementLog.isDebugEnabled()) { return ConnectionLogger.newInstance(connection, statementLog, queryStack); } else { return connection; } }
(2) 接着调用到 org.mybatis.spring.transaction.SpringManagedTransaction#getConnection
public Connection getConnection() throws SQLException { if (this.connection == null) { openConnection(); } return this.connection; } private void openConnection() throws SQLException { this.connection = DataSourceUtils.getConnection(this.dataSource); this.autoCommit = this.connection.getAutoCommit(); this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource); if (LOGGER.isDebugEnabled()) { LOGGER.debug( "JDBC Connection [" + this.connection + "] will" + (this.isConnectionTransactional ? " " : " not ") + "be managed by Spring"); } }
调用openConnection() 开启连接,
1》 调用到 org.springframework.jdbc.datasource.DataSourceUtils#getConnection
public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException { try { return doGetConnection(dataSource); } catch (SQLException ex) { throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection", ex); } catch (IllegalStateException ex) { throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection: " + ex.getMessage()); } }
继续调用到org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection
public static Connection doGetConnection(DataSource dataSource) throws SQLException { Assert.notNull(dataSource, "No DataSource specified"); ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource); if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) { conHolder.requested(); if (!conHolder.hasConnection()) { logger.debug("Fetching resumed JDBC Connection from DataSource"); conHolder.setConnection(fetchConnection(dataSource)); } return conHolder.getConnection(); } // Else we either got no holder or an empty thread-bound holder here. logger.debug("Fetching JDBC Connection from DataSource"); Connection con = fetchConnection(dataSource); if (TransactionSynchronizationManager.isSynchronizationActive()) { try { // Use same Connection for further JDBC actions within the transaction. // Thread-bound object will get removed by synchronization at transaction completion. ConnectionHolder holderToUse = conHolder; if (holderToUse == null) { holderToUse = new ConnectionHolder(con); } else { holderToUse.setConnection(con); } holderToUse.requested(); TransactionSynchronizationManager.registerSynchronization( new ConnectionSynchronization(holderToUse, dataSource)); holderToUse.setSynchronizedWithTransaction(true); if (holderToUse != conHolder) { TransactionSynchronizationManager.bindResource(dataSource, holderToUse); } } catch (RuntimeException ex) { // Unexpected exception from external delegation call -> close Connection and rethrow. releaseConnection(con, dataSource); throw ex; } } return con; }
这里可以看到有两个逻辑。
第一种:调用TransactionSynchronizationManager.getResource(dataSource) 获取ConnectionHolder, 逻辑如下
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; } private static Object doGetResource(Object actualKey) { Map<Object, Object> map = resources.get(); if (map == null) { return null; } Object value = map.get(actualKey); // Transparently remove ResourceHolder that was marked as void... if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) { map.remove(actualKey); // Remove entire ThreadLocal if empty... if (map.isEmpty()) { resources.remove(); } value = null; } return value; }
这里实际上就是从ThreadLocal 中获取之前设置的ConnectionHolder, 然后直接返回去该Connection 对象。这里实际上就是用了AOP修改设置之后的Connection 对象。
第二种: 如果不在AOP事务环境中,调用org.springframework.jdbc.datasource.DataSourceUtils#fetchConnection 获取Connection, 获取到事务之后返回去
private static Connection fetchConnection(DataSource dataSource) throws SQLException { Connection con = dataSource.getConnection(); if (con == null) { throw new IllegalStateException("DataSource returned null from getConnection(): " + dataSource); } return con; }
3. AOP事务执行提交以及重置Connection
(1) AOP自动提交
org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction 最后有两步
第一步: cleanupTransactionInfo(txInfo); 还原现场
protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) { if (txInfo != null) { txInfo.restoreThreadLocalStatus(); } }
org.springframework.transaction.interceptor.TransactionAspectSupport.TransactionInfo#restoreThreadLocalStatus
private void restoreThreadLocalStatus() { // Use stack to restore old transaction TransactionInfo. // Will be null if none was set. transactionInfoHolder.set(this.oldTransactionInfo); }
第二步: commitTransactionAfterReturning(txInfo); 提交事务
1》 org.springframework.transaction.interceptor.TransactionAspectSupport#commitTransactionAfterReturning
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) { if (txInfo != null && txInfo.getTransactionStatus() != null) { if (logger.isTraceEnabled()) { logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]"); } txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); } }
2》 org.springframework.transaction.support.AbstractPlatformTransactionManager#commit
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); } 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); } }View Code
可以看到这里进行判断,如果是status.isNewTransaction() 是新起的事务就提交(如果是第二个事务加入的事务环境不进行提交, 等执行权回到新起的事务再提交)。 调用org.springframework.jdbc.datasource.DataSourceTransactionManager#doCommit 提交
@Override 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); } }
(2) 重新设置Connection 以及进行相关的环境清理工作
org.springframework.jdbc.datasource.DataSourceTransactionManager#doCleanupAfterCompletion, 可以看到是重新设为自动提交
@Override 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(); }
可以看到是重置为自动提交,然后清除相关缓存。调用链如下:
2. 事务的传播特性
前面研究了基于AOP事务的主要特性, 在AOP环境中通过获取到Connection之后,修改为手动提交,然后dao 操作的事务从是事务管理器获取到connection, aop 在环绕通知的后半部分进行清理工作,进行事务的提交以及后续Connection 的修复。
下面研究其默认的PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRES_NEW 的差距, 也就是在单个事务以及多个事务环境是如何操作Connection 的。
1. PROPAGATION_REQUIRED 研究
默认的传播行为是这个REQUIRED, 也就是有事务就加入事务,没有事务就开启事务。也可以理解为其事务调用是一个嵌套的调用, 其过程如下:
tx6开启事务(AOP环境)->调用tx1(使用原有事务)->tx1 AOP环境清理(由于TransactionInfo 不是新事务,所以基本不做清理和提交) -> tx6 AOP环境清理(提交,清理)
下面研究其过程。
代码如下:(tx6 里面调用tx1, tx6 是第一个事务,tx1 是第二个事务)
@Override @Transactional public void tx6() { Message message = new Message(); message.setTitle("tx6"); messageMapper.insert(message); MessageService2 messageService = (MessageService2) AopContext.currentProxy(); messageService.tx1(); } @Override @Transactional public void tx1() { Message message = new Message(); message.setTitle("tx1"); messageMapper.insert(message); }
代码追踪:
1. tx6 方法执行走AOP, 从org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction 事务的入口,开启事务,调用链如下
1》 org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction 这里获取到的conHolder 为null
2》 org.springframework.jdbc.datasource.DataSourceTransactionManager#isExistingTransaction 判断是否在事务环境,返回为false, 然后走
3》 org.springframework.transaction.support.AbstractPlatformTransactionManager#startTransaction 开启事务,开启事务之前先调用org.springframework.transaction.support.AbstractPlatformTransactionManager#suspend暂停上一个事务(上一个此时为null), 内部调用org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin 获取Connection, 然后开启事务之后设置到环境上下文
4》 org.springframework.transaction.interceptor.TransactionAspectSupport#prepareTransactionInfo 准备事务信息,org.springframework.transaction.interceptor.TransactionAspectSupport.TransactionInfo#bindToThread 绑定到当前线程环境,同时记录下之前的事务信息
2. 接下来开始invocation.proceedWithInvocation(); 开始执行tx6 方法内部的 代码, 也就是在事务环境中执行相关业务逻辑, 然后会走tx1 相关的业务逻辑,其中tx1 也走的是代理类,也会走AOP的逻辑。
1》 调用到org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction, 在这里获取到的conHolder 为上tx6 AOP环境中设置的connectionHolder
2》isExistingTransaction(transaction) 判断是否是在事务环境中,返回true
3》 org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction 代码调用到这里, 这里就是根据当前新的AOP环境的事务传播行为选择不同的处理
/** * Create a TransactionStatus for an existing transaction. */ 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 { return startTransaction(definition, transaction, debugEnabled, suspendedResources); } 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. return startTransaction(definition, transaction, debugEnabled, null); } } // 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); }View Code
这里调用到:
org.springframework.transaction.support.AbstractPlatformTransactionManager#prepareTransactionStatus
protected final DefaultTransactionStatus prepareTransactionStatus( TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction, boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) { DefaultTransactionStatus status = newTransactionStatus( definition, transaction, newTransaction, newSynchronization, debug, suspendedResources); prepareSynchronization(status, definition); return status; }
这里的newTransaction 为false, 也就是当前事务信息不是新事务。然后调用org.springframework.transaction.support.AbstractPlatformTransactionManager#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(); } }View Code
4》org.springframework.transaction.interceptor.TransactionAspectSupport#prepareTransactionInfo 准备事务信息, 绑定到线程环境中,同时记录下原来的TransactionInfo 对象, 最后tx1 对应的transactionInfo 如下:
实际上用的Connection 对象和tx6 是同一个对象, 所以达到加入tx6 事务的效果。然后用oldTransactionInfo 记录下原来的tx6 事务,用于当tx1 aop环境结束后恢复tx6 的事务环境。
3. 接下来invocation.proceedWithInvocation(); 开始走tx1 的方法逻辑, 也就是执行业务代码
4. 接下来tx1 方法AOP环境中执行清理工作
1》 finally 代码块执行cleanupTransactionInfo(txInfo);
调用到 org.springframework.transaction.interceptor.TransactionAspectSupport.TransactionInfo#restoreThreadLocalStatus, 实际上就是把ThreadLocal 中事务又恢复成事务tx6 方法的信息
private void restoreThreadLocalStatus() { // Use stack to restore old transaction TransactionInfo. // Will be null if none was set. transactionInfoHolder.set(this.oldTransactionInfo); }
2》 调用commitTransactionAfterReturning 提交事务
调用 org.springframework.transaction.support.AbstractPlatformTransactionManager#processCommit
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); } }
这个内部调用org.springframework.transaction.support.AbstractPlatformTransactionManager#cleanupAfterCompletion, 根据当前事务是否是新事务以及是否有暂停的事务进行清除或者是恢复暂停的事务操作。
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()); } }
5. 接下来tx6 方法AOP环境中执行清理工作 , 和上面过程一样,只是事务是新的事务,所以在最后提交的时候会进行commit, 并且将connection 信息设置为初始化状态。
2. PROPAGATION_REQUIRES_NEW 研究
也就是新起的事务,查看下是如何做到事务隔离的。
测试代码如下:
@Override @Transactional public void tx6() { Message message = new Message(); message.setTitle("tx6"); messageMapper.insert(message); MessageService2 messageService = (MessageService2) AopContext.currentProxy(); messageService.tx1(); } @Override @Transactional(propagation = Propagation.REQUIRES_NEW) public void tx1() { Message message = new Message(); message.setTitle("tx1"); messageMapper.insert(message); }
1. tx6 事务AOP 开启过程同上面
2. tx6 AOP环境中调用 invocation.proceedWithInvocation(); 开始业务代码
3. tx1 AOP环境开始执行
1》 org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction 获取事务状态,isExistingTransaction 返回为true
2》 调用org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction 根据新事务传播行为做处理
第一步: org.springframework.transaction.support.AbstractPlatformTransactionManager#suspend 挂起事务
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; } }
第二步: 调用org.springframework.transaction.support.AbstractPlatformTransactionManager#startTransaction 开启事务
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; }
可以看到是将原来的事务生成为一个SuspendedResourcesHolder 对象, 然后存到当前事务信息中。
最终生成的TransactionInfo 对象如下:
4. tx1 执行完之后开始开始提交和清理工作,提交完调用到org.springframework.transaction.support.AbstractPlatformTransactionManager#cleanupAfterCompletion
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()); } }
resume 恢复原事务:(实际就是恢复原来的事务环境)
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); } } }
5. tx6 继续执行,然后继续后续事务逻辑, 执行tx6 事务的提交以及后续清理工作。
总结:
org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction是事务AOP开始的地方, 相关方法从这里开始。
1. AOP对dao 的影响:
AOP在业务代码执行前走AOP逻辑修改Connection 对象的autoCommit 为false, 然后生成ConnectionHolder 对象缓存到ThreadLocal 中, dao 操作的时候调用org.mybatis.spring.transaction.SpringManagedTransaction#getConnection 获取Connection 对象,最终实际也是先从上面的ThreadLocal 中获取,获取不到则证明不在事务环境中,则直接向数据源索要Connection
2. 默认的事务传播特性Propagation.REQUIRED的逻辑
1. 第一个事务AOP业务执行 , 业务代码执行前开启事务
2. 反射调用方法,开始业务逻辑: 方法内部如果有其他使用Connection的非AOP代码,则使用的是改过的Connection 对象,也就是事务生成; 如果新起的事务也是默认的REQUIRED
3. 第二个事务AOP业务代码执行前会调用org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction 获取TransactionStatus, 这里获取到的实际是在AOP环境中的; 然后根据其传播行为是REQUIRED, 然后调用org.springframework.transaction.support.AbstractPlatformTransactionManager#prepareTransactionStatus 开启一个新事务状态,只不过其newTransaction 为false; 然后
4. 第二个事务AOP 执行完之后进行提交以及清理代码逻辑,由于不是 newTransaction, 所以不进行commit操作以及清理操作
5. 代码执行权回到第一个事务AOP逻辑,第一个事务是newTransaction, 所以执行commit 操作以及恢复Connection 操作。
3. 传播特性Propagation.REQUIRED_NEW 逻辑
1、2 步骤同上面;
3. 第二个事务AOP业务代码执行前会调用org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction 获取TransactionStatus, 这里获取到的实际是在AOP环境中。 然后调用org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction (这里也是根据传播特性进行不同处理的业务逻辑), 这里suspend 挂起事务并且获取到一个SuspendedResourcesHolder 对象, 然后startTransaction 开启新的事务
4. 第二个事务AOP 执行完之后进行提交以及清理代码逻辑,由于是 newTransaction, 所以进行commit操作以及清理操作。 提交完清理过程中在org.springframework.transaction.support.AbstractPlatformTransactionManager#cleanupAfterCompletion 操作过程中,调用org.springframework.transaction.support.AbstractPlatformTransactionManager#resume 恢复事务环境
5. 代码执行权回到第一个事务AOP逻辑,第一个事务是newTransaction, 所以执行commit 操作以及恢复Connection 操作。