1、工作原理
在调用serivce服务之前通过AOP判断,方法是读库操作还是写库操作。根据判断方法名,调用不同的数据库。例如使用query、find、get等开头的方法就访问读库,其他的访问写库。
2、配置文件配置多数据源
<bean id="dataSource" class="xyz.chanjkf.aop.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>
<bean id="slave01DataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<constructor-arg>
<bean class="com.zaxxer.hikari.HikariConfig">
<property name="driverClassName" value="${hibernate.master.connection.driverClass}"/>
<property name="jdbcUrl" value="${hibernate.master.connection.url}"/>
<property name="username" value="${hibernate.master.connection.username}"/>
<property name="password" value="${hibernate.master.connection.password}"/>
<property name="readOnly" value="false" />
<property name="connectionTimeout" value="${jdbc.connectionTimeout}"/>
<property name="idleTimeout" value="${jdbc.idleTimeout}"/>
<property name="maxLifetime" value="${jdbc.maxLifetime}"/>
<property name="maximumPoolSize" value="${jdbc.maximumPoolSize}"/>
</bean>
</constructor-arg>
</bean>
<bean id="masterDataSource"
class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<constructor-arg>
<bean class="com.zaxxer.hikari.HikariConfig">
<property name="driverClassName" value="${hibernate.slave.connection.driverClass}"/>
<property name="jdbcUrl" value="${hibernate.slave.connection.url}"/>
<property name="username" value="${hibernate.slave.connection.username}"/>
<property name="password" value="${hibernate.slave.connection.password}"/>
<property name="readOnly" value="false" />
<property name="connectionTimeout" value="${jdbc.connectionTimeout}"/>
<property name="idleTimeout" value="${jdbc.idleTimeout}"/>
<property name="maxLifetime" value="${jdbc.maxLifetime}"/>
<property name="maximumPoolSize" value="${jdbc.maximumPoolSize}"/>
</bean>
</constructor-arg>
</bean>
在配置文件dataSource中指定使用自己实现的数据源DynamicDataSource
3、配置aop切面
<bean class="xyz.chanjkf.aop.dataSourceAspect" id="dataSourceAspect">
<aop:aspectj-autoproxy proxy-target-class="true" />
<aop:config expose-proxy="true">
<!--<aop:pointcut id="allManagerMethod" expression="execution(* xyz.chanjkf.service..*.*(..))" />-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="allManagerMethod"/>
<aop:aspect ref="dataSourceAspect" order="1">
<aop:pointcut id="allManagerMethod" expression="execution(* xyz.chanjkf.service..*.*(..))" />
<aop:before method="before" pointcut-ref="allManagerMethod"/>
</aop:aspect>
</aop:config>
4、aop切面
public class dataSourceAspect {
public void before(JoinPoint point){
String methodName = point.getSignature().getName();
if (isSalve(methodName)){
DynamicDataSourceHolder.setSlave();
} else {
DynamicDataSourceHolder.setMaster();
}
}
private boolean isSalve(String methodName) {
String[] salveMethod = new String[]{"find","get","query"};
return StringUtils.startsWithAny(methodName,salveMethod);
}
}
5、配置数据源工具类
public class DynamicDataSourceHolder {
/**
* 使用ThreadLocal技术来记录当前线程中的数据源的key,保证线程安全
*/
//写库对应的数据源key
private static final String MASTER = "master";
//读库对应的数据源key
private static final String SLAVE = "slave";
private static final ThreadLocal<String>local = new ThreadLocal<String>();
private static void saveDataBaseKey(String key) {
local.set(key);
}
public static void setSlave(){
saveDataBaseKey(SLAVE);
}
public static void setMaster() {
saveDataBaseKey(MASTER);
}
public static String getKey() {
return local.get();
}
}
6、自定义数据源
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceHolder.getKey();
}
}