Spring dynamic multi-source source analysis and interpretation

First, why study Spring dynamic multi-data sources

At the beginning, the initial reason was that I wanted to abstract the answering service that sent the subjective answer data to the correcting middleware, but this part was mainly sent to the correcting middleware in the way of MQ message, so I finally decided to abstract MQ. The result of the abstraction was as follows: Language, English, and general purpose tasks can personalize mq configuration, and can be extended to any business scenario where MQ is used. All the terminal needs to do is add mq configuration, customize the consumer business logic method, and call the send method.

The advantage of this is that each item that uses MQ is written mq producer, MQ consumer, send MQ data, listen MQ consumption, etc., and if there are more than one MQ configuration in one item, write this configuration multiple times. After abstraction, you only need to configure in the configuration file, and then customize the personalized business logic consumer to be ready for MQ sending.

Such a dynamic configuration of MQ, or a lot of requirements, how dynamic configuration? How can n producers and consumers of MQ be started at the time the server is started? When sending data, how do you find the correct MQ send?

In fact, I always believe that the problem I encountered must have been met by a great god, and has a mature solution. Therefore, I began to search for solutions in the industry, but failed to find them for a long time. Finally, prompted by my colleagues, I found that Spring’s idea of dynamically configuring multiple data sources was similar to the idea of dynamically configuring multiple MQ THAT I wanted to achieve. So I started spending time looking at the source of Spring’s dynamic multi-data sources.

Second, Spring dynamic multi-data source framework comb

2.1 Frame Structure

Spring dynamic multi-data sources is a component that we often use in projects, especially when doing project refactoring. There are multiple databases, and different requests may call different data sources. At this point, the specified data source needs to be dynamically invoked. Let’s take a look at Spring’s overall framework for dynamic multi-data sources

The dotted boxes in the figure above are the components of Spring dynamic multi-data sources

  1. Ds processor
  2. Aop aspects
  3. Creating a data source
  4. Dynamic data source provider
  5. Dynamically connected database

In addition, you can see the following information:

  1. Spring dynamic multi-data sources are specified by means of dynamic configuration configuration files.
  2. Spring dynamic multi-data sources supports four types of data: base data sources, JNDI data sources, DruID data sources, and Hikari data sources.
  3. Multiple trigger mechanisms: Ds is configured through headers, ds is configured through sessions, and DS is configured through SPEL. Ds is short for datasource.
  4. Support for data source nesting: a request that accesses multiple data sources is also supported, i.e., calling multiple data sources when the method is nested.

2.2 Source code Structure

Several components of Spring’s dynamic multi-data sources are perfectly represented in the source code structure.

The figure above shows the source project structure for Spring dynamic multi-data sources. We will list the main structures

----annotation: defines the DS host ---- AOP: defines a pre-notification, aspect class ---- Creator: dynamic multi-data source creator ---- Exception: ----matcher: 版 版 版 Ds processor - provider: data member provider - spring: spring dynamic data relating to launch configuration - toolkit: kit - AbstractRoutingDataSource: ----DynamicRoutingDataSource: DynamicRoutingDataSource implementation classCopy the code

2.3 Overall project structure diagram

The following figure shows the code project structure for Spring polymorphic multi-data sources.

This picture contains a lot of content, so the words are relatively small. It is possible to see that there are altogether 6 parts. Each of these sections will be explained in detail later.

Three, project source code analysis

3.1 Introducing the Spring dependent JAR package.

Spring dynamic multi-data source, when we use, directly introduce the JAR, and then configure the data source can be used. Configure the JAR package as follows

<dependency>
         <groupId>com.baomidou</groupId>
         <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
         <version>3.1.1</version>
</dependency>
Copy the code

3.2 Access to the Spring JAR package

Why would you introduce a JAR and use it in your project? This is because meta-INF /spring.factories are configured with the JAR package

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration
Copy the code

In this file, specifies the spring loaded dynamically when DynamicDataSourceAutoConfiguration to automatically scan file, the file is the entrance to the source project. Defines the project startup automatic equipment DynamicDataSourceAutoConfiguration file here.

Next, let’s take a look at DynamicDataSourceAutoConfiguration file.

3.3 Spring configuration file entry.

Below is the main content of DynamicDataSourceAutoConfiguration file.

The main purpose of the Spring configuration file is to load the related beans when the system loads them. Which beans are loaded when the project is initialized?

  1. Dynamic data source attribute class DynamicDataSourceProperties
  2. Data source processor DsProcessor adopts three methods of responsibility chain design mode to load DS
  3. Dynamic data source annotation class DynamicDataSourceAnnotationAdvisor, including pre notice, tangent plane, tangent point load
  4. Data source creator DataSourceCreator, this method is in another class DynamicDataSourceCreatorAutoConfiguration is loaded. The bean class is also automatically configured. There are four types of data sources you can choose to create.
  5. Data source Provider: dynamically initializes data sources and reads YML configuration files. One or more data sources can be configured in the configuration files.

Let’s look at the source code

1. DynamicDataSourceAutoConfiguration dynamic data source configuration file

@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(a) {
        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(a) {
        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);
        returnadvisor; }}Copy the code

This code is familiar to you. It is an annotation that automatically injects beans when the project is started. Let’s take a closer look at what he injected.

  1. DsProcess, short for datasource. Here is the main use of the chain of responsibility design pattern, obtain DS.
  2. Dynamic multiple data sources inform dynamicDatasourceAnnotationAdvisor annotations, this notice is an aop front, when a request is trigger pre notice, which used to determine whether the use of mq message queue
  3. DynamicDataSourceProvider dynamic multiple source providers, we are dynamically configure multiple data sources, then there is a process of parsing configuration, parsing configuration is done here, analyze the multiple data sources, and then call data creators to create the data source, respectively. Spring Dynamic Multi-data sources supports nesting of data sources.
  4. Dynamic routing to the DynamicRoutingDataSource. When the request comes in, the corresponding data source is found. To establish a database connection, this is where the connection is done.

Here we found that there are four bean initialization, there is no bean create the creation process, the bean’s creation is in another configuration (DynamicDataSourceCreatorAutoConfiguration) done in class.

@Slf4j
@Configuration
@AllArgsConstructor
@EnableConfigurationProperties(DynamicDataSourceProperties.class)
public class DynamicDataSourceCreatorAutoConfiguration {

    private final DynamicDataSourceProperties properties;

    @Bean
    @ConditionalOnMissingBean
    public DataSourceCreator dataSourceCreator(a) {
        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(a) {
        return new BasicDataSourceCreator();
    }

    @Bean
    @ConditionalOnMissingBean
    public JndiDataSourceCreator jndiDataSourceCreator(a) {
        return new JndiDataSourceCreator();
    }

    @Bean
    @ConditionalOnMissingBean
    public DruidDataSourceCreator druidDataSourceCreator(a) {
        return new DruidDataSourceCreator(properties.getDruid());
    }

    @Bean
    @ConditionalOnMissingBean
    public HikariDataSourceCreator hikariDataSourceCreator(a) {
        return newHikariDataSourceCreator(properties.getHikari()); }}Copy the code

Presumably because there are a lot of different types of data, it is put into a single configuration. As you can see from the above source code, there are four types of data source configurations. These are basic, JNDI, DruID, and Hikari. These four data sources are set into the DataSourceCreator using the combined design pattern.

Next, take a look at what each module does separately.