config包下
/**
* 注入动态数据源
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
}
/**
* 配置数据源并注入到 DynamicDataSource
*/
@Configuration
public class DateSourceConfig {
/**
* yml 属性
*/
@Autowired
private Environment environment;
@Autowired
UserMapper userMapper;
@Bean
@Primary
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>(); //全部数据源
// 可以循环读库加配置
// master
DataSource master = buildDataSource(
environment.getProperty("spring.datasource.dynamic.master.url"),
environment.getProperty("spring.datasource.dynamic.master.username"),
environment.getProperty("spring.datasource.dynamic.master.password"));
// slave
DataSource slave = buildDataSource(
environment.getProperty("spring.datasource.dynamic.slave.url"),
environment.getProperty("spring.datasource.dynamic.slave.username"),
environment.getProperty("spring.datasource.dynamic.slave.password"));
// 添加全部数据源
dataSourceMap.put("master", master);
dataSourceMap.put("slave", slave);
dynamicDataSource.setTargetDataSources(dataSourceMap);
// 默认数据源
dynamicDataSource.setDefaultTargetDataSource(master);
return dynamicDataSource;
}
public static DataSource buildDataSource(String url, String username, String password) {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
return dataSource;
}
}
/**
* 当前线程对应的数据源
*/
public class DataSourceContextHolder {
//此类提供线程局部变量。这些变量不同于它们的正常对应关系是每个线程访问一个线程(通过get、set方法),有自己的独立初始化变量的副本。
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setDataSource(String dataSource) {
contextHolder.set(dataSource);
}
public static String getDataSource() {
return contextHolder.get();
}
public static void clearDataSource() {
contextHolder.remove();
}
}
aop包下
/**
* 动态数据源注解
*/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DS {
String value() default "master";
}
@Aspect
@Component
@Slf4j
public class DSAspect {
@Pointcut("@annotation(com.my.config.aop.DS)")
public void dataSourcePointcut(){}
/**
* 获取DS注解并设置 master 或 slave值 设置数据源
* @param point
* @return
* @throws Throwable
*/
@Around("dataSourcePointcut()")
public Object datasourceAround(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature)point.getSignature();
Method method = signature.getMethod();
DS ds = method.getAnnotation(DS.class);
if (Objects.nonNull(ds)){
DataSourceContextHolder.setDataSource(ds.value());
}
try {
return point.proceed();
} finally {
DataSourceContextHolder.clearDataSource();
}
}
}
启动类
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
yml
spring:
datasource:
dynamic:
primary: master # 设置主数据源
master:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3307/mybatis
username: root
password: phpts
slave:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3307/mybatis_slave
username: root
password: phpts
pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- mybatis起步依赖 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>