zoukankan      html  css  js  c++  java
  • 基于spring的aop实现读写分离与事务配置

      项目开发中经常会遇到读写分离等多数据源配置的需求,在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开启事务,都会在方法体前创建连接并开启事务,事务体内的其他方法均沿用当前上下文的连接,并不会新获取连接,直到事务体运行结束,归还连接。

  • 相关阅读:
    Springx b 界面
    ETCD 分布式锁实现逻辑
    etcd 集群概述 转
    [转]MySQL性能优化
    Elasticsearch、MongoDB和Hadoop比较
    Mysql导出表格变成科学计数法的解决方案
    记一次laravel框架下的post请求测试
    Mysql语句优化记录
    laravel与MongoDB 的部分实践
    Laravel多数据库连接记录
  • 原文地址:https://www.cnblogs.com/hanganglin/p/6055392.html
Copyright © 2011-2022 走看看