<转>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"); } }