首先看Druid数据库多数据源Demo
Spring的多数据源支持---AbstractRoutingDataSource,AbstractRoutingDataSource定义了抽象的determineCurrentLookupKey方法,子类实现此方法,来确定要使用的数据源
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
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;
}
// 确定当前要使用的数据源
protected abstract Object determineCurrentLookupKey();
}
Druid 实现多数据源支持,核心是Overwrite AbstractRoutingDataSource 的 determineCurrentLookupKey 方法
public class MultipleDataSource extends AbstractRoutingDataSource {
public static final String DATA_SOURCE_A = "dataSourceTestA"; // A库
public static final String DATA_SOURCE_B = "dataSourceTestB"; // B库
private static final ThreadLocal<String> dataSourceKey = new ThreadLocal<String>();
public static void setDataSourceKey(String dataSource) {
dataSourceKey.set(dataSource);
}
@Override
protected Object determineCurrentLookupKey() {
return dataSourceKey.get();
}
public static void clear(){
dataSourceKey.remove();
}
}
AbstractRoutingDataSourceApplication实战:
DataSourceLeonProperties
package center.leon.oak.druid.abstract_routing_data_source.properties;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @program: leon
* @description:
* @author: Leon
* @create: 2021-07-22 13:56
**/
@Data
@Component
@ConfigurationProperties(value = "spring.datasource.leon")
public class DataSourceLeonProperties {
/**
* 默认数据源
*/
private DataSourceLeon defaultDataSourceLeon;
/**
* 数据源列表
*/
private List<DataSourceLeon> dataSourceLeonList;
/**
* 自定义数据源
*/
@Data
@Accessors(chain = true)
public static class DataSourceLeon {
/**
* 数据源自定义名称
*/
private String dataSourceName;
/**
* 数据源驱动
*/
private String driverClassName;
/**
* 数据源URL
*/
private String url;
/**
*
*/
private String username;
/**
*
*/
private String password;
}
}
MultipleDataSource
package center.leon.oak.druid.abstract_routing_data_source.config;
import center.leon.oak.druid.abstract_routing_data_source.properties.DataSourceLeonProperties;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* @program: leon
* @description:
* @author: Leon
* @create: 2021-07-22 11:31
**/
@Component
public class MultipleDataSource extends AbstractRoutingDataSource implements InitializingBean {
ThreadLocal<String> data_source_name = new ThreadLocal<>();
@Autowired
private DataSourceLeonProperties datasourceLeonProperties;
@Override
public void afterPropertiesSet() {
init();
}
private void init() {
// 设置默认数据源
if (null != datasourceLeonProperties.getDefaultDataSourceLeon()) {
DataSourceLeonProperties.DataSourceLeon dataSourceLeon = datasourceLeonProperties.getDefaultDataSourceLeon();
DruidDataSource druidDataSource = DruidDataSourceBuilder.create().build();
druidDataSource.setDriverClassName(dataSourceLeon.getDriverClassName());
druidDataSource.setUrl(dataSourceLeon.getUrl());
druidDataSource.setUsername(dataSourceLeon.getUsername());
druidDataSource.setPassword(dataSourceLeon.getPassword());
super.setDefaultTargetDataSource(druidDataSource);
}
// 设置多数据源
Map<Object, Object> targetDataSources = new HashMap<>();
for (DataSourceLeonProperties.DataSourceLeon dataSourceLeon : datasourceLeonProperties.getDataSourceLeonList()) {
DruidDataSource druidDataSource = DruidDataSourceBuilder.create().build();
druidDataSource.setDriverClassName(dataSourceLeon.getDriverClassName());
druidDataSource.setUrl(dataSourceLeon.getUrl());
druidDataSource.setUsername(dataSourceLeon.getUsername());
druidDataSource.setPassword(dataSourceLeon.getPassword());
targetDataSources.put(dataSourceLeon.getDataSourceName(), druidDataSource);
}
super.setTargetDataSources(targetDataSources);
// 调用父类 afterPropertiesSet 方法
// 将 targetDataSources 数据 填充到 resolvedDataSources 中
// determineCurrentLookupKey 返回的 lookupKey 本质上是在 resolvedDataSources 中查找 dataSource 返回
// 将 defaultTargetDataSource 数据 填充到 resolvedDefaultDataSource 中
// resolvedDataSources 中 查找不到合适的 dataSource 时返回。
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey() {
return data_source_name.get();
}
public void setDb_tb_name(String dataSourceName) {
data_source_name.set(dataSourceName);
}
public void clearDb_tb_name() {
data_source_name.remove();
}
}
MonkeyDO
package center.leon.oak.druid.abstract_routing_data_source.dto;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @program: leon
* @description:
* @author: Leon
* @create: 2021-07-22 12:05
**/
@Data
@TableName(value = "monkey")
@Accessors(chain = true)
public class MonkeyDO {
private Long id;
private String name;
private Long version;
}
MonkeyMapper
package center.leon.oak.druid.abstract_routing_data_source.mapper;
import center.leon.oak.druid.abstract_routing_data_source.dto.MonkeyDO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* @program: leon
* @description:
* @author: Leon
* @create: 2021-07-22 12:04
**/
@Mapper
public interface MonkeyMapper extends BaseMapper<MonkeyDO> {
}
MonkeyService
package center.leon.oak.druid.abstract_routing_data_source.service;
import center.leon.oak.druid.abstract_routing_data_source.dto.MonkeyDO;
/**
* @program: leon
* @description:
* @author: Leon
* @create: 2021-07-22 12:00
**/
public interface MonkeyService {
MonkeyDO get(Long id);
MonkeyDO getInDataSource(Long id, String dataSourceName);
}
MonkeyServiceImpl
package center.leon.oak.druid.abstract_routing_data_source.service.impl;
import center.leon.oak.druid.abstract_routing_data_source.config.MultipleDataSource;
import center.leon.oak.druid.abstract_routing_data_source.dto.MonkeyDO;
import center.leon.oak.druid.abstract_routing_data_source.mapper.MonkeyMapper;
import center.leon.oak.druid.abstract_routing_data_source.service.MonkeyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @program: leon
* @description:
* @author: Leon
* @create: 2021-07-22 12:03
**/
@Service
public class MonkeyServiceImpl implements MonkeyService {
@Autowired
private MonkeyMapper monkeyMapper;
@Autowired
private MultipleDataSource multipleDataSource;
@Override
public MonkeyDO get(Long id) {
return monkeyMapper.selectById(id);
}
@Override
public MonkeyDO getInDataSource(Long id, String dataSourceName) {
try {
multipleDataSource.setDb_tb_name(dataSourceName);
return monkeyMapper.selectById(id);
} finally {
multipleDataSource.clearDb_tb_name();
}
}
}
PrintMonkey
package center.leon.oak.druid.abstract_routing_data_source.task;
import center.leon.oak.druid.abstract_routing_data_source.dto.MonkeyDO;
import center.leon.oak.druid.abstract_routing_data_source.service.MonkeyService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* @program: leon
* @description:
* @author: Leon
* @create: 2021-07-22 21:28
**/
@Slf4j
@Component
public class PrintMonkey {
@Autowired
private MonkeyService monkeyService;
@Scheduled(fixedRate = 6 * 1000)
public void printMonkey_localhost() {
MonkeyDO monkeyDO = monkeyService.getInDataSource(1L, "localhost");
log.info("threadName : {}, monkey : {}", Thread.currentThread().getName(), monkeyDO);
}
@Scheduled(fixedRate = 6 * 1000)
public void printMonkey_www_leon_center() {
MonkeyDO monkeyDO = monkeyService.getInDataSource(1L, "www_leon_center");
log.info("threadName : {}, monkey : {}", Thread.currentThread().getName(), monkeyDO);
}
}
application-druid.properties
spring.datasource.type=center.leon.oak.druid.abstract_routing_data_source.config.MultipleDataSource
spring.datasource.leon.data-source-leon-list[0].data-source-name=localhost
spring.datasource.leon.data-source-leon-list[0].driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.leon.data-source-leon-list[0].url=jdbc:mysql://localhost:3306/zoo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false
spring.datasource.leon.data-source-leon-list[0].username=root
spring.datasource.leon.data-source-leon-list[0].password=Liang123,
spring.datasource.leon.data-source-leon-list[1].data-source-name=www_leon_center
spring.datasource.leon.data-source-leon-list[1].driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.leon.data-source-leon-list[1].url=jdbc:mysql://www.leon.center:3306/zoo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false
spring.datasource.leon.data-source-leon-list[1].username=lmy
spring.datasource.leon.data-source-leon-list[1].password=Lmy123456,
spring.task.scheduling.pool.size=2
AbstractRoutingDataSourceApplication
package center.leon.oak.druid.abstract_routing_data_source;
import center.leon.oak.druid.abstract_routing_data_source.config.MultipleDataSource;
import center.leon.oak.druid.abstract_routing_data_source.dto.MonkeyDO;
import center.leon.oak.druid.abstract_routing_data_source.properties.DataSourceLeonProperties;
import center.leon.oak.druid.abstract_routing_data_source.service.MonkeyService;
import center.leon.oak.druid.abstract_routing_data_source.service.impl.MonkeyServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* @program: leon
* @description:
* @author: Leon
* @create: 2021-07-22 11:28
**/
@Slf4j
@EnableScheduling
@EnableConfigurationProperties
@SpringBootApplication
public class AbstractRoutingDataSourceApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(AbstractRoutingDataSourceApplication.class, args);
DataSourceLeonProperties dataSourceLeonProperties = run.getBean(DataSourceLeonProperties.class);
log.info("dataSourceLeonProperties : {}", dataSourceLeonProperties);
MonkeyService monkeyService = run.getBean(MonkeyServiceImpl.class);
final MultipleDataSource multipleDataSource = run.getBean(MultipleDataSource.class);
multipleDataSource.setDb_tb_name("localhost");
MonkeyDO monkey_1 = monkeyService.get(1L);
log.info("monkey_1 : {}", monkey_1);
multipleDataSource.clearDb_tb_name();
multipleDataSource.setDb_tb_name("www_leon_center");
MonkeyDO monkey_2 = monkeyService.get(1L);
log.info("monkey_2 : {}", monkey_2);
multipleDataSource.clearDb_tb_name();
}
}
Console
2021-07-22 22:11:39.813 INFO 69282 --- [ main] d.a.AbstractRoutingDataSourceApplication : show Time ...
2021-07-22 22:11:39.813 INFO 69282 --- [ main] d.a.AbstractRoutingDataSourceApplication : dataSourceLeonProperties : DataSourceLeonProperties(defaultDataSourceLeon=null, dataSourceLeonList=[DataSourceLeonProperties.DataSourceLeon(dataSourceName=localhost, driverClassName=com.mysql.cj.jdbc.Driver, url=jdbc:mysql://localhost:3306/zoo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false, username=root, password=Liang123,), DataSourceLeonProperties.DataSourceLeon(dataSourceName=www_leon_center, driverClassName=com.mysql.cj.jdbc.Driver, url=jdbc:mysql://www.leon.center:3306/zoo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false, username=lmy, password=Lmy123456,)])
2021-07-22 22:11:39.864 INFO 69282 --- [ scheduling-2] com.alibaba.druid.pool.DruidDataSource : {dataSource-2} inited
2021-07-22 22:11:39.864 INFO 69282 --- [ main] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} inited
2021-07-22 22:11:40.026 INFO 69282 --- [ main] d.a.AbstractRoutingDataSourceApplication : monkey_1 : MonkeyDO(id=1, name=write, version=2)
2021-07-22 22:11:40.026 INFO 69282 --- [ scheduling-1] c.l.o.d.a.task.PrintMonkey : threadName : scheduling-1, monkey : MonkeyDO(id=1, name=write, version=2)
2021-07-22 22:11:40.067 INFO 69282 --- [ scheduling-2] c.l.o.d.a.task.PrintMonkey : threadName : scheduling-2, monkey : MonkeyDO(id=1, name=monkey sun, version=500)
2021-07-22 22:11:40.075 INFO 69282 --- [ main] d.a.AbstractRoutingDataSourceApplication : monkey_2 : MonkeyDO(id=1, name=monkey sun, version=500)
2021-07-22 22:11:40.367 INFO 69282 --- [on(3)-127.0.0.1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2021-07-22 22:11:40.367 INFO 69282 --- [on(3)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2021-07-22 22:11:40.367 TRACE 69282 --- [on(3)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.multipart.support.StandardServletMultipartResolver@42d0e41
2021-07-22 22:11:40.367 TRACE 69282 --- [on(3)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver@48f2054d
2021-07-22 22:11:40.367 TRACE 69282 --- [on(3)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.servlet.theme.FixedThemeResolver@6d6cd1e0
2021-07-22 22:11:40.368 TRACE 69282 --- [on(3)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Detected DefaultRequestToViewNameTranslator
2021-07-22 22:11:40.369 TRACE 69282 --- [on(3)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Detected SessionFlashMapManager
2021-07-22 22:11:40.369 DEBUG 69282 --- [on(3)-127.0.0.1] o.s.web.servlet.DispatcherServlet : enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data
2021-07-22 22:11:40.369 INFO 69282 --- [on(3)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 2 ms
2021-07-22 22:11:45.812 INFO 69282 --- [ scheduling-1] c.l.o.d.a.task.PrintMonkey : threadName : scheduling-1, monkey : MonkeyDO(id=1, name=write, version=2)
2021-07-22 22:11:45.824 INFO 69282 --- [ scheduling-2] c.l.o.d.a.task.PrintMonkey : threadName : scheduling-2, monkey : MonkeyDO(id=1, name=monkey sun, version=500)
2021-07-22 22:11:51.808 INFO 69282 --- [ scheduling-1] c.l.o.d.a.task.PrintMonkey : threadName : scheduling-1, monkey : MonkeyDO(id=1, name=write, version=2)
2021-07-22 22:11:51.822 INFO 69282 --- [ scheduling-2] c.l.o.d.a.task.PrintMonkey : threadName : scheduling-2, monkey : MonkeyDO(id=1, name=monkey sun, version=500)
2021-07-22 22:11:57.807 INFO 69282 --- [ scheduling-1] c.l.o.d.a.task.PrintMonkey : threadName : scheduling-1, monkey : MonkeyDO(id=1, name=write, version=2)
2021-07-22 22:11:57.818 INFO 69282 --- [ scheduling-2] c.l.o.d.a.task.PrintMonkey : threadName : scheduling-2, monkey : MonkeyDO(id=1, name=monkey sun, version=500)
2021-07-22 22:12:03.809 INFO 69282 --- [ scheduling-1] c.l.o.d.a.task.PrintMonkey : threadName : scheduling-1, monkey : MonkeyDO(id=1, name=write, version=2)
2021-07-22 22:12:04.051 INFO 69282 --- [ scheduling-2] c.l.o.d.a.task.PrintMonkey : threadName : scheduling-2, monkey : MonkeyDO(id=1, name=monkey sun, version=500)
2021-07-22 22:12:09.811 INFO 69282 --- [ scheduling-1] c.l.o.d.a.task.PrintMonkey : threadName : scheduling-1, monkey : MonkeyDO(id=1, name=write, version=2)
2021-07-22 22:12:09.824 INFO 69282 --- [ scheduling-2] c.l.o.d.a.task.PrintMonkey : threadName : scheduling-2, monkey : MonkeyDO(id=1, name=monkey sun, version=500)
2021-07-22 22:12:15.810 INFO 69282 --- [ scheduling-1] c.l.o.d.a.task.PrintMonkey : threadName : scheduling-1, monkey : MonkeyDO(id=1, name=write, version=2)
2021-07-22 22:12:15.821 INFO 69282 --- [ scheduling-2] c.l.o.d.a.task.PrintMonkey : threadName : scheduling-2, monkey : MonkeyDO(id=1, name=monkey sun, version=500)