Application添加注解: @Import({DynamicDataSourceRegister.class})
application.xml配置从数据源:
slave: datasource: names: name1 ,name2 name1: driver-class-name: com.mysql.jdbc.Driver names: datasource1 url: username: password: type: com.alibaba.druid.pool.DruidDataSource name2: driver-class-name: com.mysql.jdbc.Driver names: datasource2 url: username: password: type: com.alibaba.druid.pool.DruidDataSource
数据源注册 DynamicDataSourceRegister.java
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware { private org.slf4j.Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class); public static Environment environment; //指定默认数据源(springboot2.0默认数据源是hikari如何想使用其他数据源可以自己配置) private static final String DATASOURCE_TYPE_DEFAULT = "com.zaxxer.hikari.HikariDataSource"; //默认数据源 private DataSource defaultDataSource; //用户自定义数据源 private Map<String, DataSource> slaveDataSources = new HashMap<>(); @Override public void setEnvironment(Environment environment) { initDefaultDataSource(environment); initSlaveDataSources(environment); this.environment = environment; } private void initDefaultDataSource(Environment env) { // 读取主数据源 Map<String, Object> dsMap = new HashMap<>(); dsMap.put("driver", env.getProperty("spring.datasource.driver-class-name")); dsMap.put("url", env.getProperty("spring.datasource.url")); dsMap.put("username", env.getProperty("spring.datasource.username")); dsMap.put("password", env.getProperty("spring.datasource.password")); defaultDataSource = buildDataSource(dsMap); } private void initSlaveDataSources(Environment env) { // 读取配置文件获取更多数据源 String dsPrefixs = env.getProperty("slave.datasource.names"); if(StringUtil.isNotEmpty(dsPrefixs)) { for (String dsPrefix : dsPrefixs.split(",")) { // 多个数据源 Map<String, Object> dsMap = new HashMap<>(); dsMap.put("driver", env.getProperty("slave.datasource." + dsPrefix + ".driver-class-name")); dsMap.put("url", env.getProperty("slave.datasource." + dsPrefix + ".url")); dsMap.put("username", env.getProperty("slave.datasource." + dsPrefix + ".username")); dsMap.put("password", env.getProperty("slave.datasource." + dsPrefix + ".password")); DataSource ds = buildDataSource(dsMap); slaveDataSources.put(dsPrefix, ds); } } } @Override public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) { Map<Object, Object> targetDataSources = new HashMap<Object, Object>(); //添加默认数据源 targetDataSources.put("dataSource", this.defaultDataSource); DynamicDataSourceContextHolder.dataSourceIds.add("dataSource"); //添加其他数据源 targetDataSources.putAll(slaveDataSources); for (String key : slaveDataSources.keySet()) { DynamicDataSourceContextHolder.dataSourceIds.add(key); } //创建DynamicDataSource GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(DynamicDataSource.class); beanDefinition.setSynthetic(true); MutablePropertyValues mpv = beanDefinition.getPropertyValues(); mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource); mpv.addPropertyValue("targetDataSources", targetDataSources); //注册 - BeanDefinitionRegistry beanDefinitionRegistry.registerBeanDefinition("dataSource", beanDefinition); logger.info("Dynamic DataSource Registry"); } public DataSource buildDataSource(Map<String, Object> dataSourceMap) { try { Object type = dataSourceMap.get("type"); if (type == null) { type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource } Class<? extends DataSource> dataSourceType; dataSourceType = (Class<? extends DataSource>) Class.forName((String) type); String driverClassName = dataSourceMap.get("driver").toString(); String url = dataSourceMap.get("url").toString(); String username = dataSourceMap.get("username").toString(); String password = dataSourceMap.get("password").toString(); // 自定义DataSource配置 DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url) .username(username).password(password).type(dataSourceType); return factory.build(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } }
动态数据源上下文管理:DynamicDataSourceContextHolder.java
public class DynamicDataSourceContextHolder {//存放当前线程使用的数据源类型信息 private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); //存放数据源id public static List<String> dataSourceIds = new ArrayList<String>(); //设置数据源 public static void setDataSourceType(String dataSourceType) { contextHolder.set(dataSourceType); } //获取数据源 public static String getDataSourceType() { return contextHolder.get(); } //清除数据源 public static void clearDataSourceType() { contextHolder.remove(); } //判断当前数据源是否存在 public static boolean isContainsDataSource(String dataSourceId) { return dataSourceIds.contains(dataSourceId); } }
动态数据源 DynamicDataSource.java -- AbstractRoutingDataSource(每执行一次数据库,动态获取DataSource)
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceType(); } }
动态数据源通知 DynamicDataSourceAspect.java
@Aspect @Order(-1)//保证在@Transactional之前执行 @Component public class DynamicDataSourceAspect {//改变数据源 public void changeDataSource( String targetDataSource) { if (StringUtil.isNotEmpty(targetDataSource) && DynamicDataSourceContextHolder.isContainsDataSource(targetDataSource)) { DynamicDataSourceContextHolder.setDataSourceType(targetDataSource); } } public void clearDataSource( String targetDataSource) { DynamicDataSourceContextHolder.clearDataSourceType(); } }
换源API调用:
@Autowired private DynamicDataSourceAspect dynamicDataSourceAspect;
使用: dynamicDattaSourceAspect.changeDataSource(##数据源名称##); -----查询SQL业务----- dynamicDattaSourceAspect.clearDataSource(##数据源名称##);