<转>Spring AOP依据JdbcTemplate方法名动态设置数据源

<转>Spring AOP根据JdbcTemplate方法名动态设置数据源

转载: Spring AOP根据JdbcTemplate方法名动态设置数据源

http://blog.****.net/yubaoma2014/article/details/12427885
作者: yubaoma2014

 

有删节.

 

目的: 

1. 配置多个数据源

2. 根据不同的数据源执行不同的数据操作方法

3. 事务管理?

 

  • 多数据源配置
        <!-- 主数据源, 用于执行写入操作 -->
        <bean id="masterDataSource"
            class="org.apache.commons.dbcp.BasicDataSource"
            destroy-method="close">
            <property name="driverClassName"
                value="${jdbc.driverClassName}" />
            <property name="url" value="${jdbc.url}" />
            <property name="username" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
            <property name="poolPreparedStatements" value="true" />
            <property name="defaultAutoCommit" value="true" />
        </bean>
        <!-- 从数据源, 用于执行查询操作 -->
        <bean id="slaveDataSource"
            class="org.apache.commons.dbcp.BasicDataSource"
            destroy-method="close">
            <property name="driverClassName"
                value="${jdbc.driverClassName}" />
            <property name="url" value="${jdbc.url2}" />
            <property name="username" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
            <property name="poolPreparedStatements" value="true" />
            <property name="defaultAutoCommit" value="true" />
        </bean>
    
        <!-- 动态数据源 -->
        <bean id="dataSource"
            class="test.my.serivce.ds.DynamicDataSource">
            <!-- 配置一个数据源Map, 注意这里的key值, 后面很多地方都使用到 -->
            <property name="targetDataSources">
                <map>
                    <entry key="master" value-ref="masterDataSource" />
                    <entry key="slave" value-ref="slaveDataSource" />
                </map>
            </property>
            <!-- 动态数据源默认使用的数据源 -->
            <property name="defaultTargetDataSource" ref="masterDataSource" />
        </bean>
    
    <!-- 配置JdbcTemplate -->
    <bean id="jdbcTemplate"
            class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource" />
    </bean>
    BEAN "dataSource" 是自定义类DynamicDataSource的一个实例, 而DynamicDataSource则是继承了Spring的抽象类AbstractRoutingDataSource, 顾名思义, 这是一个数据源的路由/查找类.
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    // 自定义类
    public class DynamicDataSource extends AbstractRoutingDataSource {
        
        @Override
        // 返回当前需要使用的数据源的key
        protected Object determineCurrentLookupKey() {
            // 
            return CustomerContextHolder.getCustomerType();
        }
    }
     
    //  AbstractRoutingDataSource 的部分源码
    
    public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
            
            // 重写了 getConnection() 方法
         	@Override
    	public Connection getConnection() throws SQLException {
    		return determineTargetDataSource().getConnection();
    	}
    
            // 设置当前使用的目标数据源
    	protected DataSource determineTargetDataSource() {
    		Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
                    // 目标数据源的key.
    		Object lookupKey = determineCurrentLookupKey();
                    // 获取数据源
    		DataSource dataSource = this.resolvedDataSources.get(lookupKey);
    		if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
    			dataSource = this.resolvedDefaultDataSource;
    		}
    		if (dataSource == null) {
    			throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
    		}
    		return dataSource;
    	}
            
            // 抽象方法, 返回目标数据源对应的key
            // 这里的key对应于在BEAN "dataSource"中配置的属性"targetDataSources"中的key
            protected abstract Object determineCurrentLookupKey();
    }
     
    // CustomerContextHolder, 一个ThreadLocal实现, 用于管理key
    // ThreadLocal是线程安全的, 且线程之间不能共享
    public class CustomerContextHolder {
        private static final ThreadLocal contextHolder = new ThreadLocal();
        public static void setCustomerType(String customerType) {
            contextHolder.set(customerType);
        }
        public static String getCustomerType() {
            return (String) contextHolder.get();
        }
        public static void clearCustomerType() {
            contextHolder.remove();
        }
    }
     
  • 利用AOP, 实现不同的数据源执行不同的数据操作方法
        <bean id="ba" class="test.my.serivce.ds.BeforeAdvice" />
        <!-- 强制使用CGLIB代理 -->
        <aop:config proxy-target-class="true">
    
            <!-- 切面一 主数据源执行写入操作 -->
            <aop:aspect ref="ba">
                <!-- 切点, 与数据库update相关的函数 -->
                <aop:pointcut id="update"
                    expression="execution(* org.springframework.jdbc.core.JdbcTemplate.update*(..)) || execution(* org.springframework.jdbc.core.JdbcTemplate.batchUpdate(..))" />
                <!-- 前置通知, 执行数据源设置操作 -->
                <aop:before method="setMasterDataSource"
                    pointcut-ref="update" />
            </aop:aspect>
    
            <!-- 切面二 从数据源执行查询操作 -->
            <aop:aspect ref="ba">
                <aop:before method="setSlaveDataSource"
                    pointcut="execution(* org.springframework.jdbc.core.JdbcTemplate.query*(..)) || execution(* org.springframework.jdbc.core.JdbcTemplate.execute(..))" />
            </aop:aspect>
        </aop:config>
        
     BEAN "ba"相关的类
    public class BeforeAdvice {
        public void setMasterDataSource() {
            CustomerContextHolder.setCustomerType("master");
        }
        public void setSlaveDataSource() {
            CustomerContextHolder.setCustomerType("slave");
        }
    }