关于项目配置,以前的博客都有,我就不多提了,我只发有差异的地方
package com.fileServer.datasource;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* Created by creekhan on 6/13/16.
*/
@Aspect
@Order(value = -9999)
@Component
public class DynamicDataSourceAop {
private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSourceAop.class);
private static final String[] slaveMethodPreix = {"get", "query", "find", "select", "count","page","isContain","list","check"};
@Before(" execution( * com.fileServer.serviceimpl..*.*(..) ) or execution( * com.appcore.service.impl.*.*(..) ) ")
public void checkTransaction(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
if (StringUtils.startsWithAny(methodName, slaveMethodPreix)) {
LOGGER.info("当前方法名【{}】切换的数据源【{}】",new Object[] { methodName,"SLAVE"});
DynamicDataSourceHolder.setDataSourceKey(DynamicDataSourceHolder.DataSoureKey.SLAVE);
} else {
LOGGER.info("当前方法名【{}】切换的数据源【{}】",new Object[] { methodName,"MASTER"});
DynamicDataSourceHolder.setDataSourceKey(DynamicDataSourceHolder.DataSoureKey.MASTER);
}
}
}
package com.fileServer.datasource;
/**
* Created by creekhan on 6/13/16.
*/
public class DynamicDataSourceHolder {
private static final ThreadLocal<DataSoureKey> dataSoureThreadLocal = new ThreadLocal<>();
public static String getDataSourceKey() {
String persisName = "";
DataSoureKey dataSoureKey = dataSoureThreadLocal.get();
if (dataSoureKey == null) {
persisName = DataSoureKey.MASTER.getKey();
} else {
persisName = dataSoureKey.getKey();
}
return persisName;
}
public static void setDataSourceKey(DataSoureKey dataSource) {
dataSoureThreadLocal.set(dataSource);
}
public static enum DataSoureKey {
MASTER {
@Override
String getKey() {
return DATASOURCE_MASTER;
}
}, SLAVE {
@Override
String getKey() {
return DATASOURCE_SALVE;
}
}, ADS {
@Override
String getKey() {
return DATASOURCE_ADS;
}
};
private static final String DATASOURCE_MASTER = "master";
private static final String DATASOURCE_SALVE = "slave";
private static final String DATASOURCE_ADS = "ads";
abstract String getKey();
}
}
package com.fileServer.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* Created by creekhan on 6/13/16.
*/
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
String key = DynamicDataSourceHolder.getDataSourceKey();
return key;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd
">
<!-- 加载配置 -->
<context:property-placeholder location="classpath:jdbc.properties" ignore-unresolvable="true" />
<!-- 创建数据源 -->
<bean id="masterDataSource" class="com.alibaba.druid.pool.DruidDataSource" >
<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="maxActive" value="${pool.maxPoolSize}" />
<!-- 超过时间限制是否回收 -->
<property name="removeAbandoned" value="true" />
<!-- 超时时间;单位为秒。180秒=3分钟 -->
<property name="removeAbandonedTimeout" value="${pool.removeAbandonedTimeout}" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="${pool.maxWait}" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="${pool.timeBetweenEvictionRunsMillis}" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="${pool.minEvictableIdleTimeMillis}" />
<property name="validationQuery" value="${pool.validationQuery} " />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
</bean>
<!-- 创建数据源 -->
<bean id="slaveDataSource" class="com.alibaba.druid.pool.DruidDataSource" >
<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="maxActive" value="${pool.maxPoolSize}" />
<!-- 超过时间限制是否回收 -->
<property name="removeAbandoned" value="true" />
<!-- 超时时间;单位为秒。180秒=3分钟 -->
<property name="removeAbandonedTimeout" value="${pool.removeAbandonedTimeout}" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="${pool.maxWait}" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="${pool.timeBetweenEvictionRunsMillis}" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="${pool.minEvictableIdleTimeMillis}" />
<property name="validationQuery" value="${pool.validationQuery} " />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
</bean>
<bean id="daynamicDataSource" class="com.fileServer.datasource.DynamicRoutingDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="master" value-ref="masterDataSource"/>
<entry key="slave" value-ref="slaveDataSource"/>
</map>
</property>
</bean>
</beans>
核心思路:先初始化多个数据源的bean,然后通过AOP切面切换数据库对应的key(这里一定得用ThreadLocal这个类来对多线程的数据源Key做隔离处理)以及继承AbstractRoutingDataSource来实现多数据源的路由,AOP切面获取joinPoint.getSignature().getName()方法名,再根据自定义的规则做相应的处理。数据库master只允许写入操作,从库只允许读操作,从库和主库在通过mysql 的binLog日志来实现数据同步