<!-- 數據源的配置 --> <bean id="masterHikariConfig" class="com.zaxxer.hikari.HikariConfig"> <property name="poolName" value="springHikariCP"/> <property name="connectionTestQuery" value="SELECT 1"/> <property name="dataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlDataSource"/> <property name="dataSourceProperties"> <props> <prop key="url">${jdbc.master.url}</prop> <prop key="user">${jdbc.master.username}</prop> <prop key="password">${jdbc.master.password}</prop> </props> </property> <property name="autoCommit" value="true"/> </bean> <bean id="slave01HikariConfig" class="com.zaxxer.hikari.HikariConfig"> <property name="poolName" value="springHikariCP"/> <property name="connectionTestQuery" value="SELECT 1"/> <property name="dataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlDataSource"/> <property name="dataSourceProperties"> <props> <prop key="url">${jdbc.slave01.url}</prop> <prop key="user">${jdbc.slave01.username}</prop> <prop key="password">${jdbc.slave01.password}</prop> </props> </property> <property name="autoCommit" value="true"/> </bean> <!--主數據源--> <bean id="masterDataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close"> <constructor-arg ref="masterHikariConfig"/> </bean> <!--從數據源--> <bean id="slave01DataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close"> <constructor-arg ref="slave01HikariConfig"/> </bean> <!--定義dynamic數據源--> <bean id="dataSource" class="com.xwtec.activity.datasource.DynamicDataSource"> <!-- 設置多個數據源 --> <property name="targetDataSources"> <map key-type="java.lang.String"> <!-- 這個key需要和程序中的key一致 --> <entry key="master" value-ref="masterDataSource"/> <entry key="slave" value-ref="slave01DataSource"/> </map> </property> <!-- 設置默認的數據源,這裏默認走寫庫 --> <property name="defaultTargetDataSource" ref="masterDataSource"/> </bean> <!-- 定義AOP切面處理器 --> <bean class="com.xwtec.activity.datasource.DataSourceAspect" id="dataSourceAspect"/> <!-- 配置數據庫註解aop --> <bean id="dynamicDataSourceAspect" class="com.xwtec.activity.datasource.DataSourceAspect" /> <aop:config> <aop:aspect id="c" ref="dynamicDataSourceAspect"> <!--如果只針對活動,則配置改爲execution(* com.xwtec.activity.dao.*.*(..))--> <aop:pointcut id="route" expression="execution(* com.xwtec.*.dao.*.*(..))"/> <aop:before pointcut-ref="route" method="before"/> </aop:aspect> </aop:config> <!-- 配置數據庫註解aop -->
/**
* 定義數據源的AOP切面,通過該Service的方法名判斷是應該走讀庫還是寫庫
*
*/
public class DataSourceAspect {
/**
* 在進入Service方法之前執行
*
* @param point 切面對象
*/
public void before(JoinPoint point) {
// 獲取到當前執行的方法名
String methodName = point.getSignature().getName();
if (isSlave(methodName)) {
// 標記爲讀庫
DynamicDataSourceHolder.markSlave();
} else {
// 標記爲寫庫
DynamicDataSourceHolder.markMaster();
}
}
/**
* 判斷是否爲讀庫
*
* @param methodName
* @return
*/
private Boolean isSlave(String methodName) {
// 方法名以query、find、get開頭的方法名走從庫
return StringUtils.startsWithAny(methodName, new String[]{"query", "find", "get","select"});
}
/**
* 定義動態數據源,實現通過集成Spring提供的AbstractRoutingDataSource,只需要實現determineCurrentLookupKey方法即可
* <p>
* 由於DynamicDataSource是單例的,線程不安全的,所以採用ThreadLocal保證線程安全,由DynamicDataSourceHolder完成。
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
// 使用DynamicDataSourceHolder保證線程安全,並且得到當前線程中的數據源key
return DynamicDataSourceHolder.getDataSourceKey();
}
}
/**
* 使用ThreadLocal技術來記錄當前線程中的數據源的key
*/
public class DynamicDataSourceHolder {
//寫庫對應的數據源key
private static final String MASTER = "master";
//讀庫對應的數據源key
private static final String SLAVE = "slave";
//使用ThreadLocal記錄當前線程的數據源key
private static final ThreadLocal<String> holder = new ThreadLocal<String>();
/**
* 設置數據源key
*
* @param key
*/
public static void putDataSourceKey(String key) {
holder.set(key);
}
/**
* 獲取數據源key
*
* @return
*/
public static String getDataSourceKey() {
return holder.get();
}
/**
* 標記寫庫
*/
public static void markMaster() {
putDataSourceKey(MASTER);
}
/**
* 標記讀庫
*/
public static void markSlave() {
putDataSourceKey(SLAVE);
}