Spring提供了抽象类AbstractRoutingDataSource,允许动态设置数据源,从而实现对多数据源进行数据读写操作。
1、首先需要定义一个类继承抽象类AbstractRoutingDataSource,并实现相应的方法determineCurrentLookupKey:
protected Object determineCurrentLookupKey() { }
决定使用哪个LookupKey作为目前的数据源key。
2、写一个辅助类,用于实现数据源的选择、切换,由于数据源是多线程共享的,需要确保线程安全,可以使用ThreadLocal来保存每个线程的数据源key。
public class DataSourceSelector{
private static final ThreadLocal<String> dataSourceHolder = new ThreadLocal<String>();
public static void setDataSource1() { dataSourceHolder.set(“dataSource1”); }
public static void setDataSource2() { dataSourceHolder.set(“dataSource1”); }
}
3、在spring配置文件中配置多个数据源信息:
<bean id="dataSource1">
<property name="driverClass" value="" />
<property name="jdbcUrl" value="" />
<property name="user" value="" />
<property name="password" value="" />
</bean>
<bean id="dataSource2">
<property name="driverClass" value="" />
<property name="jdbcUrl" value="" />
<property name="user" value="" />
<property name="password" value="" />
</bean>
<bean id="dataSource" class="com.xx.dao.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="dataSource1" value-ref="dataSource1" />
<entry key="dataSource2" value-ref="dataSource2" />
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSource2" />
</bean>
DynamicDataSource即为第一步中实现抽象类AbstractRoutingDataSource的具体选择数据源的类。
4、在需要切换数据源的时候,调用DataSourceSelector类的方法设置需要的数据源就可以了。
在此基础上,还可以结合spring AOP实现mysql等数据库的读写分离,为此需要定义一个类实现MethodBeforeAdvice,AfterReturningAdvice,ThrowsAdvice,如下:
public class DataSourceAdvice implements MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
// 方法执行之前,设置数据源 String methodName = method.getName(); if (methodName.startsWith("get") || method.getName().startsWith("select")) DataSourceSelector.setSlave(); else DataSourceSelector.setMaster(); }public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable { }
public void afterThrowing(Method method, Object[] args, Object target, Exception ex) throws Throwable {DataSourceSelector.setSlave();}
}
需要在Spring配置文件里增加通知的配置项:
<!-- 切换数据源拦截器,拦截com.xx.dao.impl下的所有类的所有方法 -->
<bean id="dataSourceAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <bean class="com.xx.dao.dynamic.DataSourceAdvice"/> </property> <property name="patterns"> <list> <value>.*</value> </list> </property> </bean> <!-- 所有以DaoImpl结尾的bean都拦截以自动选择数据库 --> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames" value="*DaoImpl" /> <property name="interceptorNames"> <list> <value>dataSourceAdvisor</value> </list> </property> </bean>DaoImpl里的所有以get或者select开头的方法都会从slave数据库读取数据,而其他方法,例如以insert、update开头的方法都会往master数据库里存数据,从而实现读写分离。