这里上具体代码,因为没时间做单独的可运行实例,所以这里只贴关键的代码。
DataSource的配置
<bean id="parentDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="defaultAutoCommit" value="${hibernate.defaultAutoCommit}" />
<property name="maxTotal" value="${hibernate.maxTotal}" />
<property name="maxIdle" value="${hibernate.maxIdle}" />
<property name="initialSize" value="${hibernate.initialSize}" />
<property name="testOnBorrow" value="${hibernate.testOnBorrow}" />
<property name="validationQuery" value="${hibernate.validationQuery}" />
<property name="connectionInitSqls" value="set names utf8mb4" />
</bean>
<bean id="masterDataSource" class="org.apache.commons.dbcp2.BasicDataSource" parent="parentDataSource">
<property name="driverClassName" value="${jdbc.master.driver}" />
<property name="url" value="${jdbc.master.url}" />
<property name="username" value="${jdbc.master.username}" />
<property name="password" value="${jdbc.master.password}" />
</bean>
<bean id="slaveDataSource" class="org.apache.commons.dbcp2.BasicDataSource" parent="parentDataSource">
<property name="driverClassName" value="${jdbc.slave.driver}" />
<property name="url" value="${jdbc.slave.url}" />
<property name="username" value="${jdbc.slave.username}" />
<property name="password" value="${jdbc.slave.password}" />
</bean>
ReplicationRoutingDataSource为AbstractRoutingDataSource的实现
<bean id="dataSource" class="com.xxx.xxx.ReplicationRoutingDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="MASTER" value-ref="masterDataSource"/>
<entry key="SLAVE" value-ref="slaveDataSource"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="masterDataSource"/>
</bean>
ReplicationRoutingDataSource 写法
public class ReplicationRoutingDataSource extends AbstractRoutingDataSource {
private Map<Object, Object> tempTargetDataSources = null;
public ReplicationRoutingDataSource(){
DynamicDataSourceAspect.replicationRoutingDataSource = this;
}
@Override
public void setTargetDataSources(Map<Object, Object> targetDataSources) {
super.setTargetDataSources(targetDataSources);
tempTargetDataSources = targetDataSources;
}
public Map<Object, Object> getTargetDataSources() {
return tempTargetDataSources;
}
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
DataSource的动态管理类通过ThreadLocal保证线程安全
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
public static List<String> dataSourceIds = new ArrayList<>();
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
}
public static String getDataSourceType() {
return contextHolder.get();
}
public static void clearDataSourceType() {
contextHolder.remove();
}
/**
* 判断指定DataSrouce当前是否存在
*
* @param dataSourceId
* @return
* @author zxl
* @create
*/
public static boolean containsDataSource(String dataSourceId){
return dataSourceIds.contains(dataSourceId);
}
}
自定义注解
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
String name();
}
切面类,通过自定义注解TargetDataSource作为切入点
public class DynamicDataSourceAspect {
private Logger logger = Logger.getLogger(DynamicDataSourceAspect.class);
public static ReplicationRoutingDataSource replicationRoutingDataSource = null;
public void changeDataSource(JoinPoint point, TargetDataSource ds) throws Throwable {
String dsId = ds.name();
if (replicationRoutingDataSource!=null
&&!replicationRoutingDataSource.getTargetDataSources().containsKey(dsId)) {
logger.error("DataSource not exist "+ds.name() + point.getSignature());
} else {
logger.debug("Use DataSource : {} > {}"+ds.name()+ point.getSignature());
DynamicDataSourceContextHolder.setDataSourceType(ds.name());
}
}
public void restoreDataSource(JoinPoint point, TargetDataSource ds) {
logger.debug("Revert DataSource : {} > {}"+ds.name()+ point.getSignature());
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
AOP配置,这里请注意,由于配置到Contorller层的缘故,如果发现配置无效,请检查是否出现AOP配置冲突
<!-- 指定DataSourceAOP,用于动态切换DataSource -->
<bean id="dataSourceAspect" class="com.xxx.xxx.DynamicDataSourceAspect" />
<aop:config>
<aop:aspect id="aspectDataSourceConfig" ref="dataSourceAspect" >
<aop:before method="changeDataSource" pointcut="@annotation(ds)"/>
<aop:after method="restoreDataSource" pointcut="@annotation(ds)"/>
</aop:aspect>
</aop:config>
使用方法
@Controller
@RequestMapping(value = "/test")
public class TestController extends BaseController {
@RequestMapping(value = "/testmaster", method = {RequestMethod.POST, RequestMethod.GET })
@ResponseBody
@TargetDataSource(name="SLAVE")
public String onTestSlave(HttpServletRequest request, HttpServletResponse response) {
//具体业务
return "result";
}
@RequestMapping(value = "/testslave", method = {RequestMethod.POST, RequestMethod.GET })
@ResponseBody
@TargetDataSource(name="MASTER")
public String onTestMaster(HttpServletRequest request, HttpServletResponse response) {
//具体业务
return "result";
}
}
参考
1. http://blog.youkuaiyun.com/caomiao2006/article/details/38989789
2. http://stackoverflow.com/questions/30023704/database-routing-using-abstractroutingdatasource