代码改变世界

基于spring的aop实现读写分离与事务配置

2016-11-11 19:23  hduhans  阅读(1770)  评论(0编辑  收藏  举报

  项目开发中经常会遇到读写分离等多数据源配置的需求,在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);
            ...
            
    }

}
View Code

  可以看出方法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);
        ...
    }

}
View Code

  如果当前事务未开启,则通过方法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) {
            ...
        }
        
    }
    
}
View Code

  执行到这里事务已经开启,继续事务体内执行其他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);
            }
        }
    }
    
}
View Code

  所以,使用Spring开启事务,都会在方法体前创建连接并开启事务,事务体内的其他方法均沿用当前上下文的连接,并不会新获取连接,直到事务体运行结束,归还连接。