使用场景
主要在多租户场景中,常常新的一个租户进来需要动态的添加一个数据源到库中,使得系统不用重启即可切换数据源。
如何实现
引入相关依赖,版本用最新的就行,注意引入 parent
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <!-- 版本管理,由spring-boot统一整合管理版本 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid-version}</version>
</dependency>
<!-- 重点是这个包,这里有个天坑,2.x版本手动排除依赖不行!!建议使用parent标签 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${dynamic-datasource-version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus-version}</version>
</dependency>
</dependencies>
|
多数据源配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | spring:
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
lazy: true #默认false非懒启动,系统加载到数据源立即初始化连接池
datasource:
master:
url: jdbc:mysql://192.168.10.46:3306/xxx?useUnicode=true&characterEncoding=utf-8
username: xxx
password: xxx
driver-class-name: com.mysql.cj.jdbc.Driver
slave_1:
url: jdbc:mysql://192.168.10.46:3306/xxx?useUnicode=true&characterEncoding=utf-8
username: xxx
password: xxx
driver-class-name: com.mysql.cj.jdbc.Driver
slave_2:
url: jdbc:mysql://192.168.10.46:3306/xxx?useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=true
username: xxx
password: xxx
driver-class-name: com.mysql.cj.jdbc.Driver
|
常规的多数据源配置方式如上所示,原理大致是每次启动时,将这些配置加载到某个地方进行管理,而要实现动态数据源,就需要直接操作,将数据源放入到这个个地方。因此,dynamic-datasource 提供了这么一个接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public interface DataSourceCreator {
/**
* 通过属性创建数据源
*
* @param dataSourceProperty 数据源属性
* @return 被创建的数据源
*/
DataSource createDataSource(DataSourceProperty dataSourceProperty);
/**
* 当前创建器是否支持根据此属性创建
*
* @param dataSourceProperty 数据源属性
* @return 是否支持
*/
boolean support(DataSourceProperty dataSourceProperty);
}
|
而由于接入方式的不同,会有不同的实现类,这些实现类都在 creator 包下

我们来看两个常用的实现类
- DruidDataSourceCreator:适配了 Druid 数据源
- DefaultDataSourceCreator:这是一个通用的创建器,其根据环境自动选择连接池,默认的顺序为 druid>hikaricp>beecp>dbcp>spring basic
动态数据源使用
添加数据源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @Autowired
private DataSource dataSource;
@Autowired
private DefaultDataSourceCreator dataSourceCreator;
private void cacheAddSourceDTO(DataSourceDTO dto) {
DataSourceProperty dataSourceProperty = new DataSourceProperty();
// 添加至缓存
BeanUtils.copyProperties(dto, dataSourceProperty);
DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
// 默认的顺序为druid>hikaricp>beecp>dbcp>spring basic
DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty);
ds.addDataSource(dto.getPoolName(), dataSource);
}
|
删除数据源
1 2 | DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource; ds.removeDataSource(poolName); |
源码解析
动态添加数据源的源码比较简单,一句话概括来说就是添加数据源的配置!下面是 druid 的实现

可以看到对外暴露了三个方法,主要关注 doCreateDataSource 即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | public DataSource doCreateDataSource(DataSourceProperty dataSourceProperty) {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername(dataSourceProperty.getUsername());
dataSource.setPassword(dataSourceProperty.getPassword());
dataSource.setUrl(dataSourceProperty.getUrl());
dataSource.setName(dataSourceProperty.getPoolName());
String driverClassName = dataSourceProperty.getDriverClassName();
if (!StringUtils.isEmpty(driverClassName)) {
dataSource.setDriverClassName(driverClassName);
}
DruidConfig config = dataSourceProperty.getDruid();
Properties properties = config.toProperties(gConfig);
List<Filter> proxyFilters = this.initFilters(dataSourceProperty, properties.getProperty("druid.filters"));
dataSource.setProxyFilters(proxyFilters);
dataSource.configFromPropety(properties);
//连接参数单独设置
dataSource.setConnectProperties(config.getConnectionProperties());
//设置druid内置properties不支持的的参数
this.setParam(dataSource, config);
if (Boolean.FALSE.equals(dataSourceProperty.getLazy())) {
try {
dataSource.init();
} catch (SQLException e) {
throw new ErrorCreateDataSourceException("druid create error", e);
}
}
return dataSource;
}
|
不难看出,返回类型为 DataSource,是 jdk 中 javax.sql 包提供的,用来连接数据库的实现。而前面所有的操作都是用来封装成 DataSource 这个对象的。
在实际使用过程中,有这么一行代码 (DynamicRoutingDataSource) dataSource,不难猜出,这里是管理所有数据源的地方,操作了这里才能实现动态添加。

版权声明:本文为huofuman960209原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。