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(##數據源名稱##);