上篇文章中已经借助 DynamicDataSourceBuilder
类从配置文件中解析得到了默认数据源和动态数据源,接下来需要配置动态数据源的“本体”,并借助 AOP 动态的切换数据源。
配置动态数据源
AbstractRoutingDataSource
实现了 InitializingBean
接口,在 afterPropertiesSet
方法中通过处理 targetDataSources
和 defaultTargetDataSource
中得到最终的 resolvedDefaultDataSource
和 resolvedDataSources
,我们的实现类(动态数据源“本体”)需要覆写该方法,从而给targetDataSources
和 defaultTargetDataSource
进行赋值。
可以看到第 118 行如果
targetDataSources
为 null 就会抛出异常,因此我们的导出类需要先进行赋值,再调用父类方法,动态数据源配置代码如下:
@Component
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<DsKey> dataSourceKey = new InheritableThreadLocal<DsKey>() {
@Override
protected DsKey initialValue() {
return DsKey.DATASOURCE;
}
};
@Autowired
private DynamicDataSourceBuilder dynamicDataSourceBuilder;
// 维护数据源 key(Lookup key)
private static Set<String> dsKeySet = new HashSet<>();
public static DsKey getDataSourceKey() {
return dataSourceKey.get();
}
public static void setDataSourceKey(DsKey key) {
dataSourceKey.set(key);
}
static int addDsKey(String key) {
dsKeySet.add(key);
return dsKeySet.size();
}
public static void clearDataSourceType() {
dataSourceKey.remove();
}
public static boolean contains(String key) {
return dsKeySet.contains(key);
}
public static boolean contains(DsKey key) {
return dsKeySet.contains(key.getCode());
}
@Override
protected Object determineCurrentLookupKey() {
return dataSourceKey.get();
}
@Override
public void afterPropertiesSet() {
// 所有数据源,包括默认数据源
Map<String, DataSource> customDataSources = dynamicDataSourceBuilder.getTargetDataSources();
// Lookup key
customDataSources.forEach((key, da) -> addDsKey(key));
// DataSource
DataSource defaultDataSource = dynamicDataSourceBuilder.getDefaultDataSource();
setDefaultTargetDataSource(defaultDataSource);
setTargetDataSources(new HashMap<>(customDataSources));
super.afterPropertiesSet();
}
}
- DsKey 是个枚举类型,维护了所有的
ds-keys
,ThreadLocal 类型变量dataSourceKey
用于维护当前线程所使用的数据源,覆写 ThreadLocal 的 initialValue 方法以指定默认数据源。 -
dsKeySet
集合中维护了所有的Lookup key
,可提供contains
校验,覆写afterPropertiesSet
方法时为其赋值。 -
afterPropertiesSet
方法从DynamicDataSourceBuilder
中得到所有解析到的数据源,并将其赋值给targetDataSources
和defaultTargetDataSource
。 -
determineCurrentLookupKey
方法是AbstractRoutingDataSource
抽象类唯一需要导出类实现的方法,也正是该方法决定当前所用的数据源,而当前的数据源信息维护在dataSourceKey
,当我们需要修改当前数据源时只需要修改dataSourceKey
的值即可(通过调用setDataSourceKey
方法)。
AOP 的方式进行动态切换
定义注解供 AOP 进行拦截,以切换数据源:
image.png
在使用了 DataSource 注解的地方进行数据源切换,方法调用结束时恢复默认数据源:
在具体业务中如果需要更细粒度的控制数据源,也可直接调用
DynamicDataSource.setDataSourceKey
方法设置数据源,同时在结束操作时需要调用 DynamicDataSource.clearDataSourceType
方法恢复默认数据源。
使用示例
public class TestService {
@DataSource(dsKey = DsKey.DB1)
public void test() {
//
}
@DataSource(dsKey = DsKey.DB1)
public void test1() {
DynamicDataSource.setDataSourceKey(DsKey.DATASOURCE);
// do somthing
DynamicDataSource.setDataSourceKey(DsKey.DB1);
}
}
代码已上传 GitHub,可以在 这里 找到
网友评论