大致思路
创建用于标志数据源的自定义注解
通过配置切面,在操作数据库的方法之前,扫描该方法的注解所配置的数据源名称,将名称存储在一个代表当前线程变量工具类
创建AbstarctRoutingDataSource子类DynamicDataSourc,重写determineCurrentLookupKey()方法,把当前线程变量的工具类存储的数据源名称返回即可。
在spring配置文件中,将多个数据源配置到我们创建的DynamicDataSourc
一、配置双数据源 xml配置文件
master.driver=com.mysql.cj.jdbc.Driver
master.url=jdbc:mysql://10.70.20.81:3306/test
master.username=root
master.password=root
slave.driver=com.mysql.cj.jdbc.Driver
slave.url=jdbc:mysql://10.70.20.155:3306/test
slave.username=root
slave.password=root
<!-- master数据源 -->
<beanid="masterDataSource"class="com.alibaba.druid.pool.DruidDataSource">
<propertyname="driverClassName"value="${master.driver}"></property>
<propertyname="url"value="${master.url}"></property>
<propertyname="username"value="${master.username}"></property>
<propertyname="password"value="${master.password}"></property>
</bean>
<!-- slave数据源 -->
<beanid="slaveDataSource"class="com.alibaba.druid.pool.DruidDataSource">
<propertyname="driverClassName"value="${slave.driver}"></property>
<propertyname="url"value="${slave.url}"></property>
<propertyname="username"value="${slave.username}"></property>
<propertyname="password"value="${slave.password}"></property>
</bean>
二、定义用来切库的注解和枚举类
注解定义
importjava.lang.annotation.ElementType;
importjava.lang.annotation.Retention;
importjava.lang.annotation.RetentionPolicy;
importjava.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public@interfaceDataSwitch {
DataTypevalue() defaultDataType.master;
}
枚举类
publicenumDataType {
master("masterDataSource"),slave("slaveDataSource");
privateStringvalue;
DataType(Stringname){
this.value=name;
}
publicStringgetValue() {
returnvalue;
}
publicvoidsetValue(Stringvalue) {
this.value=value;
}
}
三、定义一个当前线程的变量的工具类,用于设置对应的数据源名称
public classDynamicDataSourceHolder {
privatestaticfinalThreadLocal<String>threadLocal=newThreadLocal<String>();
publicstaticStringgetThreadLocal() {
returnthreadLocal.get();
}
publicstaticvoidsetThreadLocal(Stringname) {
threadLocal.set(name);
}
publicstaticvoidclear(){
threadLocal.remove();
}
}
四、创建AbstractRoutingDataSource的子类,并重写determineCurrentLockupKey方法
importorg.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
publicclassDynamicDataSourceextendsAbstractRoutingDataSource {
protectedObjectdetermineCurrentLookupKey() {
System.out.println("------------>"+DynamicDataSourceHolder.getThreadLocal());
returnDynamicDataSourceHolder.getThreadLocal();
}
}
五、将多数据源配置到我们创建的DynamicDataSource下
<beanid="dynamicDataSource"class="com.shen.datasource.DynamicDataSource">
<propertyname="targetDataSources">
<mapkey-type="java.lang.String">
<entrykey="masterDataSource"value-ref="masterDataSource"></entry>
<entrykey="slaveDataSource"value-ref="slaveDataSource"></entry>
</map>
</property>
<propertyname="defaultTargetDataSource"ref="masterDataSource"></property>
</bean>
六、配置切面类,在操作数据库方法之前,获取注解配置的数据源名称并返回
importcom.shen.datasource.DataSwitch;
importcom.shen.datasource.DataType;
importcom.shen.datasource.DynamicDataSourceHolder;
importorg.aspectj.lang.JoinPoint;
importorg.aspectj.lang.annotation.After;
importorg.aspectj.lang.annotation.Aspect;
importorg.aspectj.lang.annotation.Before;
importorg.aspectj.lang.annotation.Pointcut;
importorg.springframework.core.annotation.Order;
importorg.springframework.stereotype.Component;
importjava.lang.reflect.Method;
@Component
@Aspect
@Order(0)
publicclassDataSourceAspect {
@Pointcut("execution (* com.shen.service..*(..))")
publicvoidaspect(){
}
@Before("aspect()")
publicvoidbefore(JoinPointjoinPoint){
Class<?>clazz=joinPoint.getTarget().getClass();
Method[] method=clazz.getMethods();
DataSwitchdataSwitch=null;
booleanis=false;
for(Methodm:method){
if(m.isAnnotationPresent(DataSwitch.class)){
dataSwitch=m.getAnnotation(DataSwitch.class);
DynamicDataSourceHolder.setThreadLocal(dataSwitch.value().getValue());
is=true;
}
}
if(!is){
DynamicDataSourceHolder.setThreadLocal(DataType.master.getValue());
}
}
@After("aspect()")
publicvoidafter(){
DynamicDataSourceHolder.clear();
}
}
七、测试
@Service
publicclassUserService {
@Autowired
privateUserMapperuserMapper;
@DataSwitch(DataType.master)
publicIntegerinsertUser(Useruser){
System.out.println("service--------------------");
returnuserMapper.insertSelective(user);
}
@DataSwitch(DataType.slave)
publicList<User>queryAllUsers(){
returnuserMapper.selectByExample(null);
}
}