spring boot动态数据源
2017-06-27 本文已影响411人
jey恒
基于spring boot
- 支持master数据源,不需要配置从库
- 支持多个从库配置,支持从库路由策略
- 开箱即用
大概结构

使用
- @ EnableDynamicDataSource启用动态数据源,注入dataSource到事务
@EnableDynamicDataSource
@Configurable
@MapperScan(basePackageClasses = DaoConfig.class)
@EnableTransactionManagement(proxyTargetClass = true)
public class DaoConfig {
@Autowired
private DataSource dataSource;
@Bean
public PlatformTransactionManager txManager() {
return new DataSourceTransactionManager(dataSource);
}
}
- application.properties 配置动态数据源
#数据源配置
datasource.master.driver-class-name=com.mysql.jdbc.Driver
datasource.master.url=jdbc:mysql://xxxx:3306/test?useUnicode=true&characterEncoding=UTF-8
datasource.master.username=xxx
datasource.master.password=xxx
# 多个从库names=one,two,tree逗号隔开就好
datasource.slave.names=one
datasource.slave.one.driver-class-name=com.mysql.jdbc.Driver
datasource.slave.one.url=jdbc:mysql://xxxx:3306/test_slave?useUnicode=true&characterEncoding=UTF-8
datasource.slave.one.username=xxx
datasource.slave.one.password=xxx
# 数据注册公用配置 【druid,其他的数据源参考对于的属性配置即可 type配置数据源类型】
datasource.common.type=com.alibaba.druid.pool.DruidDataSource
datasource.common.initial-size=1
datasource.common.min-idle=1
datasource.common.max-active=30
datasource.common.test-on-borrow=true
datasource.common.max-wait=60000
#配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
datasource.common.timeBetweenEvictionRunsMillis=60000
#配置一个连接在池中最小生存的时间,单位是毫秒
datasource.common.minEvictableIdleTimeMillis=300000
datasource.common.testWhileIdle=true
datasource.common.testOnBorrow=false
datasource.common.testOnReturn=false
datasource.common.validationQuery=SELECT 'x'
datasource.common.poolPreparedStatements=true
datasource.common.maxOpenPreparedStatements=20
在service的实现类中使用
@SelectDataSource(type = DataSourceType.SLAVE)
public Object getxxx(){
return xxx;
}
流程
- 初始化
// 读取配置文件,注册DynamicRoutingDataSource
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware
- 路由
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
/** 主库 */
@Getter
@Setter
private DataSource master;
/** 多个从库 */
@Getter
@Setter
private List<DataSource> slaveList;
/** 路由策略 */
@Getter
@Setter
private DataSourceRoute dataSourceRoute;
}
- 然后通过 DynamicDatasourceAspect 类aop拦截注解
@Order(1)
@Slf4j
@Aspect
public class DynamicDatasourceAspect {
// 切点 @Pointcut("@annotation(com.yyfq.io.common.datasource.SelectDataSource)")
public void aspectMethod() {}
/**
* 切点 环绕
*
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("aspectMethod()")
public Object dynamicDataSource(ProceedingJoinPoint joinPoint) throws Throwable {
try {
SelectDataSource dataSource = get(joinPoint);
if (Objects.nonNull(dataSource)) {
DataSourceType dataSourceType = dataSource.type();
DataSourceHolder.setLookUpKey(dataSourceType);
}
return joinPoint.proceed();
} catch (Throwable e) {
throw e;
} finally {
DataSourceHolder.clear();
}
}
- 再看 DataSourceHolder 用来保存当前线程的上下文
public class DataSourceHolder {
private final static ThreadLocal<DataSourceType> lookUpKey = new ThreadLocal<>();
// 方法中多个业务sql, 简单的路由缓存
private final static ThreadLocal<DataSource> nowDataSource = new ThreadLocal<>();
public static void clear() {
lookUpKey.remove();
nowDataSource.remove();
}
- 路由策略默认使用
// 模板子类实现 路由策略获取一个数据源
@Override
protected DataSource routTarget0(List<DataSource> dataSourceList) {
DataSource dataSource = dataSourceList.get(random(dataSourceList));
return dataSource;
}
private int random(List<DataSource> dataSourceList) {
if (dataSourceList.size() == 1) {
return 0;
}
return RandomUtils.nextInt(0, dataSourceList.size());
}
- 结合mybatis下一步可以做 读写分离,分库分表