1.创建DynamicDataSource并继承AbstractRoutingDataSource,实现determineCurrentLookupKey方法
public final class DynamicDataSource extends AbstractRoutingDataSource implements ApplicationContextAware {
private static final String DATA_SOURCES_NAME = "targetDataSources";
private ApplicationContext applicationContext;
@Override
protected Object determineCurrentLookupKey() {
DataSourceBeanBuilder dataSourceBeanBuilder = DataSourceHolder.getDataSource();
if (dataSourceBeanBuilder == null) {
return null;
}
DataSourceBean dataSourceBean = new DataSourceBean(dataSourceBeanBuilder);
try {
Map<Object, Object> map = null;
map = getTargetDataSources();
synchronized (map) {
if (!map.containsKey(dataSourceBean.getBeanName())) {
map.put(dataSourceBean.getBeanName(), createDataSource(dataSourceBean));
super.afterPropertiesSet();//通知spring有bean更新
}
}
} catch (NoSuchFieldException e) {
logger.info("failed:" + e);
} catch (IllegalAccessException e) {
logger.info("failed:" + e);
}
return dataSourceBean.getBeanName();
}
private Object createDataSource(DataSourceBean dataSourceBean) throws IllegalAccessException {
//在spring容器中创建并且声明bean
ConfigurableApplicationContext context = (ConfigurableApplicationContext) applicationContext;
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getBeanFactory();
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(DruidDataSource.class);
//将dataSourceBean中的属性值赋给目标bean
Map<String, Object> properties = getPropertyKeyValues(DataSourceBean.class, dataSourceBean);
for (Map.Entry<String, Object> entry : properties.entrySet()) {
beanDefinitionBuilder.addPropertyValue((String) entry.getKey(), entry.getValue());
}
beanFactory.registerBeanDefinition(dataSourceBean.getBeanName(), beanDefinitionBuilder.getBeanDefinition());
return applicationContext.getBean(dataSourceBean.getBeanName());
}
private Map<Object, Object> getTargetDataSources() throws NoSuchFieldException, IllegalAccessException {
Field field = AbstractRoutingDataSource.class.getDeclaredField(DATA_SOURCES_NAME);
field.setAccessible(true);
return (Map<Object, Object>) field.get(this);
}
private <T> Map<String, Object> getPropertyKeyValues(Class<T> clazz, Object object) throws IllegalAccessException {
Field[] fields = clazz.getDeclaredFields();
Map<String, Object> result = new HashMap<String, Object>();
for (Field field : fields) {
field.setAccessible(true);
result.put(field.getName(), field.get(object));
}
result.remove("beanName");
return result;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
}
2.threadLocal保存数据库连接信息表,保证线程安全
public final class DataSourceHolder {
private static ThreadLocal<DataSourceBeanBuilder> threadLocal=new ThreadLocal<DataSourceBeanBuilder>(){
@Override
protected DataSourceBeanBuilder initialValue() {
return null;
}
};
static DataSourceBeanBuilder getDataSource(){
return threadLocal.get();
}
public static void setDataSource(DataSourceBeanBuilder dataSourceBeanBuilder){
threadLocal.set(dataSourceBeanBuilder);
}
public static void clearDataSource(){
threadLocal.remove();
}
}
3.设置数据源上下文环境工具类
public class DataSourceContext {
public static void setDataSource(DataSourceBeanBuilder dataSourceBeanBuilder) {
DataSourceHolder.setDataSource(dataSourceBeanBuilder);
}
public static void clearDataSource() {
DataSourceHolder.clearDataSource();
}
}
4.测试
public List<Object> exectQuery4LimitQuery(DataSourceBeanBuilder dataSourceBeanBuilder, String resourceSql, long limitCount, long offset, String queryConditions, String path) {
List<Object> queryResults = new ArrayList<>();
DataSourceContext.setDataSource(dataSourceBeanBuilder);
try {
//region 查询数据
//endregion 时间格式字段处理完毕
} catch (Exception e) {
logger.error(String.format("url %s query sql error for %s", resourceSql, e));
throw new RuntimeException("querying database error");
} finally {
DataSourceContext.clearDataSource();
}
return queryResults;
}