config包下
/*** 注入动态数据源*/
public class DynamicDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {return DataSourceContextHolder.getDataSource();}
}
/*** 配置数据源并注入到 DynamicDataSource*/
@Configuration
public class DateSourceConfig {/*** yml 属性*/@Autowiredprivate Environment environment;@AutowiredUserMapper userMapper;@Bean@Primarypublic DataSource dynamicDataSource() {DynamicDataSource dynamicDataSource = new DynamicDataSource();Map<Object, Object> dataSourceMap = new HashMap<>(); //全部数据源// 可以循环读库加配置// masterDataSource master = buildDataSource(environment.getProperty("spring.datasource.dynamic.master.url"),environment.getProperty("spring.datasource.dynamic.master.username"),environment.getProperty("spring.datasource.dynamic.master.password"));// slaveDataSource 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.Driverurl: jdbc:mysql://localhost:3307/mybatisusername: rootpassword: phptsslave:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3307/mybatis_slaveusername: rootpassword: 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>