项目开发中经常会遇到读写分离等多数据源配置的需求,在Java项目中可以通过Spring AOP来实现多数据源的切换。
一、Spring事务开启流程
Spring中通常通过@Transactional来声明使用事务,我们先来研究一下Spring是如何开启事务的。调试代码,可以发现进入事务方法体内前,会进入如下代码:
public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean { protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation) throws Throwable { ... //开启事务 TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); ... //代理拦截,执行实际方法体 retVal = invocation.proceedWithInvocation(); ... //无异常,则执行事务提交 commitTransactionAfterReturning(txInfo); ... } }
可以看出方法createTransactionIfNecessary内创建了事务,断点继续追踪进入createTransactionIfNecessary方法体的tm.getTransaction(txAttr),执行代码如下:
@SuppressWarnings("serial") public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable { @Override public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { //获取当前上下文的事务 Object transaction = doGetTransaction(); ... //如果当前事务已存在,则根据事务传播行为来处理子事务 if (isExistingTransaction(transaction)) { // Existing transaction found -> check propagation behavior to find out how to behave. return handleExistingTransaction(definition, transaction, debugEnabled); } ... //这里开启事务 doBegin(transaction, definition); ... } }
如果当前事务未开启,则通过方法doBegin进行开启,方法体:
@SuppressWarnings("serial") public class DataSourceTransactionManager extends AbstractPlatformTransactionManager implements ResourceTransactionManager, InitializingBean { @Override protected void doBegin(Object transaction, TransactionDefinition definition) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; Connection con = null; try { //如果当前db连接为空,则获取并设置连接 if (txObject.getConnectionHolder() == null || txObject.getConnectionHolder().isSynchronizedWithTransaction()) { Connection newCon = this.dataSource.getConnection(); if (logger.isDebugEnabled()) { logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction"); } txObject.setConnectionHolder(new ConnectionHolder(newCon), true); } if (con.getAutoCommit()) { ... //关闭sql自动提交 con.setAutoCommit(false); } ... //设置当前事务状态已开启 txObject.getConnectionHolder().setTransactionActive(true); ... //绑定当前db连接到ThreadLocal中,key为dataSource,为后续事务体内获取连接提供支持 if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder()); } } catch (Throwable ex) { ... } } }
执行到这里事务已经开启,继续事务体内执行其他db操作,进入工具类org.springframework.jdbc.datasource.DataSourceUtils的方法doGetConnection,方法体如下:
public abstract class DataSourceUtils { public static Connection doGetConnection(DataSource dataSource) throws SQLException { ... //如果当前TransactionSynchronizationManager上下文已经存在事务连接资源,则直接使用此连接,否则往下获取连接 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(dataSource.getConnection()); } return conHolder.getConnection(); } ... //如果当前事务上下文不存在连接,则新获取 Connection con = dataSource.getConnection(); if (TransactionSynchronizationManager.isSynchronizationActive()) { //如果当前事务已开启,则将获取到的连接资源绑定到当前TransactionSynchronizationManager上下文中,供事务体内其他db操作使用 ... if (holderToUse != conHolder) { TransactionSynchronizationManager.bindResource(dataSource, holderToUse); } } } }
所以,使用Spring开启事务,都会在方法体前创建连接并开启事务,事务体内的其他方法均沿用当前上下文的连接,并不会新获取连接,直到事务体运行结束,归还连接。