1、创建多个数据源
@ConfigurationProperties+ org.springframework.boot.jdbc.DataSourceBuilder.create().build()
2、实现该接口org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
// 父类接口DataSource方法Connection getConnection(),Connection getConnection(String
// username, String password)
public abstract class AbstractRoutingDataSource {
// DataSourceLookup接口的方法DataSource getDataSource(String var1)
// DataSource dataSource = jndiTemplate.lookup(var1, DataSource.class)
// 作用根据jndiTemplate,获取DataSource对象
private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
// 储存多个数据源,包括默认数据源
private Map<Object, Object> targetDataSources;
// 储存多个数据源,包括默认数据源(解析后)
// key=lookupKey,value=dataSource
private Map<Object, DataSource> resolvedDataSources;
// 默认数据源
private Object defaultTargetDataSource;
// 默认数据源(解析后)
private DataSource resolvedDefaultDataSource;
// 设置多个数据源
public void setTargetDataSources(Map<Object, Object> targetDataSources);
// 设置默认数据源
public void setDefaultTargetDataSource(Object defaultTargetDataSource);
// Connection getConnection()通过determineTargetDataSource().getConnection()获取连接
// DataSource determineTargetDataSource()方法是最重要的方法
DataSource determineTargetDataSource() {
// 实现子类,重写determineCurrentLookupKey()方法
// 取得lookupKey
Object lookupKey = this.determineCurrentLookupKey();
// 在resolvedDataSources的map中根据lookupKey获取数据源
DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);
// 如果dataSoure为null,则将resolvedDefaultDataSource赋值给dataSource
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
// 如果dataSoure还为null,则抛出异常
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for
lookup key [" + lookupKey + "]");
} else {
//如果dataSoure!=null,则返回
return dataSource;
}
}
}
3、实现Object lookupKey = determineCurrentLookupKey()方法
@Override
protected Object determineCurrentLookupKey() {
// 动态切换数据源
return DataSourceDynamicKey.getCurrentLookupKey();
}
public class DataSourceDynamicKey {
// currentLookupKey放在ThreadLocal中
private static final ThreadLocal<String> localLookupKey = new ThreadLocal<String>();
// 设置localLookupKey的值
public static void dynamicKey(String currentLookupKey);
// 返回localLookupKey的值
public static void getCurrentLookupKey();
// 移除ThreadLocal的值
public static void remove();
}
4、自动动态切换数据
SourceDynamicKey.dynamicKey(..)实现了类调用静态方法的动态切换数据源。也可以通过注解+aop(@before)实现自动动态切换数据源。
5、学习com.baomidou:dynamic-datasoure-spring-boot-stater:2.5.6的动态数据源
5.1
# 多主多从 纯粹多库(记得设置primary) 混合配置
spring: spring: spring:
datasource: datasource: datasource:
dynamic: dynamic: dynamic:
datasource: datasource: datasource:
master_1: mysql: master:
master_2: oracle: slave_1:
slave_1: sqlserver: slave_2:
slave_2: postgresql: oracle_1:
slave_3: h2: oracle_2:
5.2 使用@DS("dsName") 切换数据源
5.3 源码分析
5.3.1 自动配置(在spring.factories中配置DynamicDataSourceAutoConfiguration类)
5.3.2 DynamicDataSourceAutoConfiguration类
public class DynamicDataSourceAutoConfiguration {
// 读取数据源属性
//
@Autowired
private DynamicDataSourceProperties properties;
// 没有DsProcessor实现对象时,创建该对象
// 1.从请求header中获取数据源key。2.从请求session中获取数据源key
// 3.从方法参数中获取数据源key。
@Bean
@ConditionalOnMissingBean
public DsProcessor dsProcessor() {
DsHeaderProcessor headerProcessor = new DsHeaderProcessor();
DsSessionProcessor sessionProcessor = new DsSessionProcessor();
DsSpelExpressionProcessor spelExpressionProcessor = new DsSpelExpressionProcessor();
headerProcessor.setNextProcessor(sessionProcessor);
sessionProcessor.setNextProcessor(spelExpressionProcessor);
// 返回headerProcessor。DsHeaderProcessor->DsSessionProcessor-
// >DsSpelExpressionProcessor
return headerProcessor;
}
@Bean
@ConditionalOnMissingBean
public DynamicDataSourceAnnotationAdvisor
dynamicDatasourceAnnotationAdvisor(DsProcessor dsProcessor) {
// dsProcessor上面返回的对象
// interceptor对象保存dsProcessor对象
DynamicDataSourceAnnotationInterceptor interceptor = new
DynamicDataSourceAnnotationInterceptor();
interceptor.setDsProcessor(dsProcessor);
// advisor对象保存interceptor对象和拦截@DS注解
DynamicDataSourceAnnotationAdvisor advisor = new
DynamicDataSourceAnnotationAdvisor(interceptor);
// 设置循序
advisor.setOrder(this.properties.getOrder());
return advisor;
}
@Bean
@ConditionalOnMissingBean
public DynamicDataSourceProvider dynamicDataSourceProvider() {
// 接口DynamicDataSourceProvider
// Map<String, DataSource> loadDataSources()加载数据源
return new YmlDynamicDataSourceProvider(this.properties);
}
@Bean
@ConditionalOnMissingBean
// 创建DynamicDataSourceCreator,设置全局的druid和hikari和publicKey
// 静态代码块 初始化druidExists,hikariExists
public DynamicDataSourceCreator dynamicDataSourceCreator() {
DynamicDataSourceCreator dynamicDataSourceCreator = new
DynamicDataSourceCreator();
dynamicDataSourceCreator.setDruidGlobalConfig(this.properties.getDruid());
dynamicDataSourceCreator.setHikariGlobalConfig(this.properties.getHikari());
dynamicDataSourceCreator.setGlobalPublicKey(this.properties.getPublicKey());
return dynamicDataSourceCreator;
}
@Bean
@ConditionalOnMissingBean
// 获取数据源
public DataSource dataSource(DynamicDataSourceProvider dynamicDataSourceProvider) {
DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
dataSource.setPrimary(this.properties.getPrimary());
dataSource.setStrategy(this.properties.getStrategy());
// 数据源提供
dataSource.setProvider(dynamicDataSourceProvider);
dataSource.setP6spy(this.properties.getP6spy());
dataSource.setStrict(this.properties.getStrict());
// springIOC创建dataSource,调用dataSource.afterPropertiesSet()
return dataSource;
}
}
@ConfigurationProperties(
prefix = "spring.datasource.dynamic"
)
public class DynamicDataSourceProperties {
// 默认master
private String primary = "master";
// 储存多个数据源
// 1.第一次初始化DataSourceProperty中publicKey为null
// 2.DataSourceProperty对象获取(get方法)username和password和url中,有decrypt(...)方法
private Map<String, DataSourceProperty> datasource = new LinkedHashMap();
// 读取spring.datasource.dynamic.druid中属性,配置druid数据源
@NestedConfigurationProperty
private DruidConfig druid = new DruidConfig();
// 读取spring.datasource.dynamic.hikari中属性,配置hikari数据源
@NestedConfigurationProperty
private HikariCpConfig hikari = new HikariCpConfig();
// 使用数据源的策略。默认平衡(原理AtomicInteger)
// RandomDynamicDataSourceStrategy
private Class<? extends DynamicDataSourceStrategy> strategy = LoadBalanceDynamicDataSourceStrategy.class;
// 默认公钥。使用公钥解码
private String publicKey = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJ4o6sn4WoPmbs7DR9mGQzuuUQM9erQTVPpwxIzB0ETYkyKffO097qXVRLA6KPmaV+/siWewR7vpfYYjWajw5KkCAwEAAQ==";
}
// 1. DsSessionProcessor @DS中的value的值为#session.datasource。则获取请求session中属性
// datasource的值
// 2. DsSpelExpressionProcessor 带有@DS("#xx")的方法method(String xx),则获取参数xx的值
public class DsHeaderProcessor {
// 抽象类DsProcessor中的属性
private DsProcessor nextProcessor;
private static final String HEADER_PREFIX = "#header";
// 重写抽象类的方法
// key为@DS中的value
public boolean matches(String key) {
return key.startsWith("#header");
}
// 重写抽象类的方法
// 从请求头datasource中获取数据源字符串。例如 key="#header.datasource"
public String doDetermineDatasource(MethodInvocation invocation, String key) {
HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
return request.getHeader(key.substring(8));
}
// 抽象类DsProcessor中的方法
public String determineDatasource(MethodInvocation invocation, String key) {
// key是否以#header开头
if (this.matches(key)) {
String datasource = this.doDetermineDatasource(invocation, key);
// 请求头中获取为null时且nextProcessor不为null,则执行nextProcessor返回
// 否则返回
return datasource == null && this.nextProcessor != null ? this.nextProcessor.determineDatasource(invocation, key) : datasource;
} else {
// 不以#header开头,nextProcessor不为null时,则执行nextProcessor返回
// 否则返回null;
return this.nextProcessor != null ? this.nextProcessor.determineDatasource(invocation, key) : null;
}
}
}
// 动态数据源切面(增强和切入点)
public class DynamicDataSourceAnnotationAdvisor {
// 增强逻辑
// advice为dynamicDataSourceAnnotationInterceptor对象
private Advice advice;
// 切入点
// this.pointcu = this.buildPointcut();
private Pointcut pointcut;
// 建立@DS的注解和方法接入点的拦截
private Pointcut buildPointcut() {
Pointcut cpc = new AnnotationMatchingPointcut(DS.class, true);
Pointcut mpc = AnnotationMatchingPointcut.forMethodAnnotation(DS.class);
return (new ComposablePointcut(cpc)).union(mpc);
}
}
// 该类为MethodInterceptor接口实现类。方法Object invoke(@Nonnull MethodInvocation var1)
// throws Throwable;
public class DynamicDataSourceAnnotationInterceptor {
private static final String DYNAMIC_PREFIX = "#";
// 解析@DS注解
private static final DynamicDataSourceClassResolver RESOLVER = new DynamicDataSourceClassResolver();
// 处理逻辑
private DsProcessor dsProcessor;
public Object invoke(MethodInvocation invocation) throws Throwable {
Object var2;
try {
// 执行方法前
// DynamicDataSourceContextHolder类.clear()则调用remove()
// ThreadLocal<Deque<String>> 接口Deque为ArrayDeque类
// push(...)左推数据源key。key为null时,推""
DynamicDataSourceContextHolder.push(this.determineDatasource(invocation));
var2 = invocation.proceed();
} finally {
// 方法执行后
// poll(...)右弹数据源key。右弹完后,执行remove()
DynamicDataSourceContextHolder.poll();
}
return var2;
}
// 先获取方法的@DS注解,没有则获取类@DS注解
// 获取@DS注解的value
private String determineDatasource(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
DS ds = method.isAnnotationPresent(DS.class) ? (DS)method.getAnnotation(DS.class) : (DS)AnnotationUtils.findAnnotation(RESOLVER.targetClass(invocation), DS.class);
String key = ds.value();
// key以#开头,执行dsProcessor。否则返回key
return !key.isEmpty() && key.startsWith("#") ? this.dsProcessor.determineDatasource(invocation, key) : key;
}
//
public class DynamicRoutingDataSource {
private static final String UNDERLINE = "_";
// 多数据源
private DynamicDataSourceProvider provider;
// 默认平衡调用数据源
private Class<? extends DynamicDataSourceStrategy> strategy;
// 储存多个数据源对象
private Map<String, DataSource> dataSourceMap = new LinkedHashMap();
private Map<String, DynamicGroupDataSource> groupDataSources = new ConcurrentHashMap();
//
public void afterPropertiesSet() throws Exception {
// YmlDynamicDataSourceProvider.loadDataSources()
//
Map<String, DataSource> dataSources = this.provider.loadDataSources()->{
Map<String, DataSourceProperty> dataSourcePropertiesMap =
this.properties.getDatasource();
return this.createDataSourceMap(dataSourcePropertiesMap)->{
dataSourceMap.put(pollName,
// 使用全局配置,创建DataSource对象(解码属性)
this.dynamicDataSourceCreator.createDataSource(dataSourceProperty));
};
};
Iterator var2 = dataSources.entrySet().iterator();
while(var2.hasNext()) {
Map.Entry<String, DataSource> dsItem = (Map.Entry)var2.next();
// 向dataSourceMap添加数据源
// 如果数据源key包含‘_’,加入groupDataSources以key.split("_")[0]加入
this.addDataSource((String)dsItem.getKey(), (DataSource)dsItem.getValue());
}
if (this.groupDataSources.containsKey(this.primary)) {
log.info("dynamic-datasource initial loaded [{}] datasource,primary group datasource named [{}]", dataSources.size(), this.primary);
} else {
if (!this.dataSourceMap.containsKey(this.primary)) {
throw new RuntimeException("dynamic-datasource Please check the setting of primary");
}
log.info("dynamic-datasource initial loaded [{}] datasource,primary datasource named [{}]", dataSources.size(), this.primary);
}
}
// 得到数据源连接
public Connection getConnection() throws SQLException {
return this.determineDataSource().getConnection();
}
// 根据threadLocal获取数据源
public DataSource determineDataSource() {
return this.getDataSource(DynamicDataSourceContextHolder.peek());
}
// 1.根据ds从map中取
// 2.ds为null时或其他情况,取主数据源(组能取到先取组)
public DataSource getDataSource(String ds) {
...
}
}