AbstractRoutingDataSource
1、原理
- 可在程序中动态设置数据源
- 给子类提供获取当前数据源名称实现,保证一个线程一个连接
- 对外提供getConnection方法,选取对应的数据源获取连接
2、使用
-
1.创建DataSourceContextHolder类,内置ThreadLocal,提供设置datasourceName和移除的方法
-
2.继承AbstractRoutingDataSource类,实现determineCurrentLookupKey方法,调用DataSourceContextHolder方法
-
3.所需切换数据源处,注入AbstractRoutingDataSource实现类,调用此三个方法重设数据源
this.dynamicDataSource.setDefaultTargetDataSource(defaultDataSource);
this.dynamicDataSource.setTargetDataSources(targetDataSources);
this.dynamicDataSource.afterPropertiesSet(); -
4.重设数据源后,DataSourceContextHolder中设置新的datasourceName
-
5.后续此线程获取数据库连接即为刚才设置的。
3、源码
// 根据当前设置的数据源,返回相应的连接
@Override
public Connection getConnection() throws SQLException {
return determineTargetDataSource().getConnection();
}
// 由继承类实现,可定义DataSourceContextHolder类,
// 使用ThreadLocal保存唯一的数据源名称,使得每个请求线程保有自己本次的数据源
@Nullable
protected abstract Object determineCurrentLookupKey();
// 根据实现的determineCurrentLookupKey方法,动态从resolvedDataSources获取当前数据源
protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
}
return dataSource;
}
// 设置当前数据源
public void setTargetDataSources(Map<Object, Object> targetDataSources) {
this.targetDataSources = targetDataSources;
}
// 设置默认数据源即主数据源
public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
this.defaultTargetDataSource = defaultTargetDataSource;
}
// 将设置的数据源放入resolvedDataSources、resolvedDefaultDataSource
@Override
public void afterPropertiesSet() {
if (this.targetDataSources == null) {
throw new IllegalArgumentException("Property 'targetDataSources' is required");
}
this.resolvedDataSources = new HashMap<>(this.targetDataSources.size());
this.targetDataSources.forEach((key, value) -> {
Object lookupKey = resolveSpecifiedLookupKey(key);
DataSource dataSource = resolveSpecifiedDataSource(value);
this.resolvedDataSources.put(lookupKey, dataSource);
});
if (this.defaultTargetDataSource != null) {
this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);
}
}