1、方向明确
拿我们最常用的mybatis来说,它获取数据库连接时就是通过获取DataSource对象的getConnection()方法来获取的,我们可以简单看一下mybatis的源码。当我们使用myabtis框架进行查询、修改操作时,它通过调用query、update等方法来操作数据库
query方法又调用了doQuery方法

而这个doQuery是调用子类SimpleExecutor中的方法


最终层层调用JdbcTransaction中的DataSource对象的getConnection方法

因此我们的方向很明确,只需要在它拿到连接的时候返回不同的DataSource对象就可以实现多数据源
2、实际操作
我们通过AOP实现在进入方法前切换到指定数据源,然后改写DataSource方法逻辑即可,可以让DataSource对象中的getConnection方法中进行处理,来获取方法指定的数据源
因此我们需要先定义一个线程上下文,这个上下文中需要记录我们在当前线程中需要使用的数据源
数据源配置
实现多数据源,必须要配置多数据源的连接信息,我们可以在配置文件中进行编写

DataSourceContextHolder
在这个上下文中,定义了三个方法,设置线程使用的数据源名称、获取、清空
方便我们后面在线程中设置和获取数据源名称
java
public class DataSourceContextHolder {
private static final ThreadLocal<String> DATA_SOURCE_THREAD_LOCAL = new ThreadLocal<>();
public static void setDataSourceName(String dataSourceName)
{
DATA_SOURCE_THREAD_LOCAL.set(dataSourceName);
}
public static String getDataSourceName()
{
return DATA_SOURCE_THREAD_LOCAL.get();
}
public static void clearDataSourceName()
{
DATA_SOURCE_THREAD_LOCAL.remove();
}
}
CustomDataSource
这个类需要继承Spring官方的AbstractRoutingDataSource来实现数据源的动态切换,我们需要重写里面的determineCurrentLookupKey方法,这个方法就是决定使用哪个数据源的key,然后调用父类的getConnection方法即可实现切换数据源
我们需要将本次的数据源信息告诉AbstractRoutingDataSource



所以我们需要返回线程上下文中的数据源名,和告诉当前项目中的数据源信息
上面定义了线程上下文,上下文中存的就是本次使用的数据源名
java
public class CustomDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
// 返回本次线程中使用的数据源名称
return DataSourceContextHolder.getDataSourceName();
}
public CustomDataSource(DataSource defaultTargetDataSource, Map<Object, Object> dataSourcesMap)
{
// 设置默认使用哪个数据源
super.setDefaultTargetDataSource(defaultTargetDataSource);
// 可以使用哪些数据源
super.setTargetDataSources(dataSourcesMap);
// 加载数据源
super.afterPropertiesSet();
}
}
DataSourceConfig
我们需要定义项目中可以使用的数据源,通过读取配置信息返回成DataSource对象,然后mybatis通过调用这个对象的getConnection方法来获取连接
DataSourceProperties对象:数据源的基本信息,因为我们自己定义了两个,所以需要两个
java
@Configuration
public class DataSourceConfig {
// ===== DB1 配置属性 =====
@Bean
@ConfigurationProperties("spring.datasource.db1")
public DataSourceProperties db1Properties() {
return new DataSourceProperties();
}
// ===== DB1 数据源 =====
@Bean
public DataSource db1DataSource() {
return db1Properties()
.initializeDataSourceBuilder()
.build();
}
// ===== DB2 配置属性 =====
@Bean
@ConfigurationProperties("spring.datasource.db2")
public DataSourceProperties db2Properties() {
return new DataSourceProperties();
}
// ===== DB2 数据源 =====
@Bean
public DataSource db2DataSource() {
return db2Properties()
.initializeDataSourceBuilder()
.build();
}
@Bean(name = "customDataSource")
@Primary // 重点:优先使用这个数据源
public CustomDataSource dataSource(DataSource db1DataSource, DataSource db2DataSource)
{
Map<Object, Object> customDataSource = new HashMap<>();
customDataSource.put("db1",db1DataSource);
customDataSource.put("db2",db2DataSource);
return new CustomDataSource(db1DataSource,customDataSource);
}
}
必须给CustomDataSource添加@Primary表示优先使用这个数据源,然后就可以通过里面的CustomDataSource的父类进行切换
但现在就可以测试了
3、结果测试
我们写三个接口进行测试,分别查db1、db2、和不指定库
这里为了测试简单就不写AOP来处理了,后续可以自己加上



成功

当我们什么数据源都没指定,就走了默认的数据源