由于源码分析内容较多,一共分为三个部分,完整内容如下:
1. Spring动态多数据源--源码分析及解读(一)
2. Spring动态多数据源--源码分析及解读(二)
3. Spring动态多数据源--源码分析及解读(三)
一、为什么要研究Spring动态多数据源
期初,最开始的原因是:想将答题服务中发送主观题答题数据给批改中间件这块抽象出来, 但这块主要使用的是mq消息的方式发送到批改中间件,所以,最后决定将mq进行抽象,抽象后的结果是:语文,英语,通用任务都能个性化的配置mq,且可以扩展到任何使用mq的业务场景上。终端需要做的就是增加mq配置,自定义消费者业务逻辑方法,调用send方法即可。
这样做的好处是:原本在每个使用到mq的项目里都要写一遍mq生产者,mq消费者,发送mq数据,监听mq消费等动作,且如果一个项目里有多个mq配置,要写多遍这样的配置。抽象后,只需要配置文件中进行配置,然后自定义个性化的业务逻辑消费者,就可以进行mq发送了。
这样一个可动态配置的mq,要求还是挺多的,如何动态配置? 如何能够在服务器启动的时候就启动n个mq的生产者和消费者? 发送数据的时候, 怎么找到正确的mq发送呢?
其实, 我一直相信, 我遇到的问题, 肯定有大神已经遇到过, 并且已经有了成熟的解决方案了. 于是, 开始搜索行业内的解决方案, 找了很久也没找到,最后在同事的提示下,发现Spring动态配置多数据源的思想和我想实现的动态配置多MQ的思想类似。于是,我开始花时间研究Spring动态多数据源的源码。
二、Spring动态多数据源框架梳理
2.1 框架结构
Spring动态多数据源是一个我们在项目中常用到的组件,尤其是做项目重构,有多种数据库,不同的请求可能会调用不同的数据源。这时,就需要动态调用指定的数据源。我们来看看Spring动态多数据源的整体框架
上图中虚线框部分是Spring动态多数据源的几个组成部分
- ds处理器
- aop切面
- 创建数据源
- 动态数据源提供者
- 动态连接数据库
除此之外,还可以看到如下信息:
- Spring动态多数据源是通过动态配置配置文件的方式来指定多数据源的。
- Spring动态多数据源支持四种类型的数据:base数据源,jndi数据源,druid数据源,hikari数据源。
- 多种触发机制:通过header配置ds,通过session配置ds,通过spel配置ds,其中ds是datasource的简称。
- 支持数据源嵌套:一个请求过来,这个请求可能会访问多个数据源,也就是方法嵌套的时候调用多数据源,也是支持的。
2.2 源码结构
Spring动态多数据源的几个组成部分,在代码源码结构中完美的体现出来。
上图是Spring动态多数据源的源码项目结构,我们主要列一下主要的结构
----annotation:定义了DS主机
----aop:定义了一个前置通知,切面类
----creator:动态多数据源的创建器
----exception:异常处理
----matcher:匹配器
----processor:ds处理器
----provider:数据员提供者
----spring:spring动态多数据源启动配置相关类
----toolkit:工具包
----AbstractRoutingDataSource:动态路由数据源抽象类
----DynamicRoutingDataSource:动态路由数据源实现类
2.3 整体项目结构图
下图是Spring多态多数据源的代码项目结构图。
这个图内容比较多,所以字比较小,大概看出一共有6个部分就可以了。后面会就每一个部分详细说明。
三、项目源码分析
3.1 引入Spring依赖jar包.
Spring动态多数据源,我们在使用的时候,直接引入jar,然后配置数据源就可以使用了。配置jar包如下
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.1.1</version>
</dependency>
3.2 Spring jar包的入口
为什么引入jar就能在项目里使用了呢?因为在jar包里配置了META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration
在这个文件里,指定了spring动态加载的时候要自动扫描的文件DynamicDataSourceAutoConfiguration,这个文件就是源码项目的入口了。这里定义了项目启动自动装备DynamicDataSourceAutoConfiguration文件。
接下来,我们就来看看DynamicDataSourceAutoConfiguration文件。
3.3、Spring配置文件入口。
下图是DynamicDataSourceAutoConfiguration文件的主要内容。
Spring配置文件主要的作用是在系统加载的时候,就加载相关的bean。这里项目初始化的时候都加载了哪些bean呢?
- 动态数据源属性类DynamicDataSourceProperties
- 数据源处理器DsProcessor,采用责任链设计模式3种方法加载ds
- 动态数据源注解类DynamicDataSourceAnnotationAdvisor,包括前置通知,切面类,切点的加载
- 数据源创建器DataSourceCreator,这个方法是在另一个类被加载的DynamicDataSourceCreatorAutoConfiguration。也是自动配置bean类。可以选择4种类型的数据源进行创建。
- 数据源提供者Provider,这是动态初始化数据源,读取yml配置文件,在配置文件中可配置1个或多个数据源。
接下来看一下源代码
1. DynamicDataSourceAutoConfiguration动态数据源配置文件
@Slf4j
@Configuration
@AllArgsConstructor
@EnableConfigurationProperties(DynamicDataSourceProperties.class)
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
@Import(value = {DruidDynamicDataSourceConfiguration.class, DynamicDataSourceCreatorAutoConfiguration.class})
@ConditionalOnProperty(prefix = DynamicDataSourceProperties.PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true)
public class DynamicDataSourceAutoConfiguration {
private final DynamicDataSourceProperties properties;
@Bean
@ConditionalOnMissingBean
public DynamicDataSourceProvider dynamicDataSourceProvider() {
Map<String, DataSourceProperty> datasourceMap = properties.getDatasource();
return new YmlDynamicDataSourceProvider(datasourceMap);
}
@Bean
@ConditionalOnMissingBean
public DataSource dataSource(DynamicDataSourceProvider dynamicDataSourceProvider) {
DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
dataSource.setPrimary(properties.getPrimary());
dataSource.setStrict(properties.getStrict());
dataSource.setStrategy(properties.getStrategy());
dataSource.setProvider(dynamicDataSourceProvider);
dataSource.setP6spy(properties.getP6spy());
dataSource.setSeata(properties.getSeata());
return dataSource;
}
@Bean
@ConditionalOnMissingBean
public DynamicDataSourceAnnotationAdvisor dynamicDatasourceAnnotationAdvisor(DsProcessor dsProcessor) {
DynamicDataSourceAnnotationInterceptor interceptor = new DynamicDataSourceAnnotationInterceptor();
interceptor.setDsProcessor(dsProcessor);
DynamicDataSourceAnnotationAdvisor advisor = new DynamicDataSourceAnnotationAdvisor(interceptor);
advisor.setOrder(properties.getOrder());
return advisor;
}
@Bean
@ConditionalOnMissingBean
public DsProcessor dsProcessor() {
DsHeaderProcessor headerProcessor = new DsHeaderProcessor();
DsSessionProcessor sessionProcessor = new DsSessionProcessor();
DsSpelExpressionProcessor spelExpressionProcessor = new DsSpelExpressionProcessor();
headerProcessor.setNextProcessor(sessionProcessor);
sessionProcessor.setNextProcessor(spelExpressionProcessor);
return headerProcessor;
}
@Bean
@ConditionalOnBean(DynamicDataSourceConfigure.class)
public DynamicDataSourceAdvisor dynamicAdvisor(DynamicDataSourceConfigure dynamicDataSourceConfigure, DsProcessor dsProcessor) {
DynamicDataSourceAdvisor advisor = new DynamicDataSourceAdvisor(dynamicDataSourceConfigure.getMatchers());
advisor.setDsProcessor(dsProcessor);
advisor.setOrder(Ordered.HIGHEST_PRECEDENCE);
return advisor;
}
}
看到这段代码,我们就比较熟悉了,这就是通过注解的方式,在项目启动的时候,自动注入bean。我们来详细看一下,他都注入了哪些内容。
- 动态多数据源预置处理器dsProcess,ds就是datasource的简称。这里主要采用的是责任链设计模式,获取ds。
- 动态多数据源注解通知dynamicDatasourceAnnotationAdvisor,这是一个aop前置通知,当一个请求发生的时候,会触发前置通知,用来确定到底使用哪一个mq消息队列
- 动态多数据源提供者dynamicDataSourceProvider,我们是动态配置多个数据源,那么就有一个解析配置的过程,解析配置就是在这里完成的,解析出多个数据源,然后分别调用数据源创建者去创建数据源。Spring动态多数据源支持数据源的嵌套。
- 动态路由到数据源DynamicRoutingDataSource,当请求过来的时候,也找到对应的数据源了,要建立数据库连接,数据库连接的操作就是在这里完成的。
我们发现在这里就有四个bean的初始化,并没有bean的create创建过程,bean的创建过程是在另一个配置类(DynamicDataSourceCreatorAutoConfiguration)中完成的。
@Slf4j
@Configuration
@AllArgsConstructor
@EnableConfigurationProperties(DynamicDataSourceProperties.class)
public class DynamicDataSourceCreatorAutoConfiguration {
private final DynamicDataSourceProperties properties;
@Bean
@ConditionalOnMissingBean
public DataSourceCreator dataSourceCreator() {
DataSourceCreator dataSourceCreator = new DataSourceCreator();
dataSourceCreator.setBasicDataSourceCreator(basicDataSourceCreator());
dataSourceCreator.setJndiDataSourceCreator(jndiDataSourceCreator());
dataSourceCreator.setDruidDataSourceCreator(druidDataSourceCreator());
dataSourceCreator.setHikariDataSourceCreator(hikariDataSourceCreator());
dataSourceCreator.setGlobalPublicKey(properties.getPublicKey());
return dataSourceCreator;
}
@Bean
@ConditionalOnMissingBean
public BasicDataSourceCreator basicDataSourceCreator() {
return new BasicDataSourceCreator();
}
@Bean
@ConditionalOnMissingBean
public JndiDataSourceCreator jndiDataSourceCreator() {
return new JndiDataSourceCreator();
}
@Bean
@ConditionalOnMissingBean
public DruidDataSourceCreator druidDataSourceCreator() {
return new DruidDataSourceCreator(properties.getDruid());
}
@Bean
@ConditionalOnMissingBean
public HikariDataSourceCreator hikariDataSourceCreator() {
return new HikariDataSourceCreator(properties.getHikari());
}
}
大概是因为考虑到数据的种类比较多,所以将其单独放到了一个配置里面。从上面的源码可以看出,有四种类型的数据源配置。分别是:basic、jndi、druid、hikari。这四种数据源通过组合设计模式被set到DataSourceCreator中。
接下来,分别来看每一个模块都做了哪些事情。