首先是配置 我这里是yml文件 properties文件的一样差不多的
一个主库 master 一个从库 slave 我这边只配2个 需要多个的 增加即可
spring:
datasource:
master:
driver-class-name: com.mysql.jdbc.Driver
url: 自己的数据库连接
username: 用户名
password: 密码
slave:
driver-class-name: com.mysql.jdbc.Driver
url: 自己的数据库连接
username: 用户名
password: 密码
然后是创建一个自定义注解
import java.lang.annotation.*;
/**
* 数据源注解
* @author seven
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
public @interface DS {
String value() default "masterDataSource";
}
继续是在同一个线程 切换数据源的配置类
import org.springframework.stereotype.Component;
/**
* 当前线程数据源
* @author seven
*/
@Component
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
// 设置数据源名
public static void setDB(String dbType) {
contextHolder.set(dbType);
}
// 获取数据源名
public static String getDB() {
return contextHolder.get();
}
// 清除数据源名
public static void clearDB() {
contextHolder.remove();
}
}
然后是比较重点的配置 简单但重量级 把配置的数据源切入
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;
/**
* 动态数据源
* @author seven
*/
@Component
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDB();
}
}
然后就是读取配置 生成DataSource的类了
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* 动态数据源配置
* @author seven
*/
@Configuration
public class DynamicDataSourceConfiguration {
@Primary
@Bean(name = "masterDataSource")
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource masterDataSource() {
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
dataSource.setName("masterDataSource");
return dataSource;
}
@Bean(name = "slaveDataSource")
@ConfigurationProperties(prefix = "spring.datasource.master")
//@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slaveDataSource() {
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
dataSource.setName("slaveDataSource");
return dataSource;
}
/**
* 动态数据源: 通过AOP在不同数据源之间动态切换
* @return
*/
@Bean(name = "dynamicDataSource")
public DataSource dataSource(@Autowired @Qualifier("masterDataSource") DataSource primery,@Autowired @Qualifier("slaveDataSource") DataSource coocon) {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 默认数据源
dynamicDataSource.setDefaultTargetDataSource(primery);
// 配置多数据源
Map<Object, Object> dsMap = new HashMap<Object, Object>(2);
dsMap.put("masterDataSource", primery);
dsMap.put("slaveDataSource", coocon);
dynamicDataSource.setTargetDataSources(dsMap);
return dynamicDataSource;
}
@Bean
public PlatformTransactionManager txManager(DataSource dynamicDataSource) {
return new DataSourceTransactionManager(dynamicDataSource);
}
@Bean
@ConfigurationProperties(prefix = "mybatis")
public SqlSessionFactoryBean sqlSessionFactoryBean(@Autowired @Qualifier("dynamicDataSource") DataSource dynamicDataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicDataSource);
return sqlSessionFactoryBean;
}
}
最后 当然是Aop 切入了 这边的切入点 是项目的service路径
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* 动态数据源AOP切面
*/
@Aspect
@Order(-1)
@Component
public class DynamicDataSourceAspect {
private Logger logger = LoggerFactory.getLogger(this.getClass());
//切点
@Pointcut("execution(* com.xxx.xxx.service..*(..))")
public void aspect() { }
@Before("aspect()")
private void before(JoinPoint point) {
Object target = point.getTarget();
String method = point.getSignature().getName();
Class<?> classz = target.getClass();// 获取目标类
Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
.getMethod().getParameterTypes();
try {
Method m = classz.getMethod(method, parameterTypes);
if (m != null && m.isAnnotationPresent(DS.class)) {
DS data = m.getAnnotation(DS.class);
logger.info("method :{},datasource:{}",m.getName() ,data.value());
DataSourceContextHolder.setDB(data.value());// 数据源放到当前线程中
}
} catch (Exception e) {
logger.error("get datasource error ",e);
//默认选择master
DataSourceContextHolder.setDB("masterDataSource");// 数据源放到当前线程中
}
}
@AfterReturning("aspect()")
public void after(JoinPoint point) {
DataSourceContextHolder.clearDB();
}
}
最后 在测试之前 需要在启动类加点东西
//指定aop事务执行顺序,已保证在切换数据源的后面
@EnableTransactionManagement(order = 2)
//排除数据源自动配置
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@Import({DynamicConfiguration.class})
然后 大功告成 测试一波 需要切换到从库的service或类 贴上自定义注解即可
@DS(value = "slaveDataSource")