SpringBoot中Druid连接池与多数据源切换的方法

 更新时间:2024年11月15日 09:37:54   作者:码到三十五  
微服务架构中多数据源切换是个常见的需求,Spring Boot 提供了强大的支持来简化这一过程.本文给大家介绍了SpringBoot中Druid连接池与多数据源切换的方法,需要的朋友可以参考下

多数据源切换原理

多数据源切换的原理主要基于 Spring 的 AbstractRoutingDataSource 类。AbstractRoutingDataSource 类允许根据运行时上下文动态选择数据源。其核心在于实现 determineCurrentLookupKey 方法,该方法决定当前操作使用哪个数据源。 AbstractRoutingDataSource 实现多数据源切换的原理:

1. 数据源映射

  • AbstractRoutingDataSource 内部维护了一个映射(Map),用于存储数据源标识(key)和对应的数据源实例(value)。这个映射允许根据数据源标识快速查找和获取对应的数据源。

2. 数据源标识的确定

  • AbstractRoutingDataSource 提供了一个抽象方法 determineCurrentLookupKey(),该方法用于确定当前需要使用的数据源标识。这个方法需要由子类实现,以返回当前线程或请求应该使用的数据源标识。

3. 数据源的选择与连接获取

  • 当应用程序需要获取数据库连接时,AbstractRoutingDataSource 的 getConnection() 方法会被调用。这个方法首先调用 determineCurrentLookupKey() 方法来获取当前的数据源标识,然后根据这个标识从内部映射中查找对应的数据源。
  • 一旦找到了对应的数据源,AbstractRoutingDataSource 就会调用该数据源的 getConnection() 方法来获取实际的数据库连接,并将这个连接返回给应用程序。

4. 数据源切换的实现

  • 为了实现数据源的动态切换,通常会在子类中重写 determineCurrentLookupKey() 方法,并根据当前的上下文(如线程变量)来确定返回的数据源标识。
  • 此外,通常会使用 ThreadLocal 来存储每个线程的数据源标识,这样每个线程都可以独立地切换数据源而不会互相干扰。

实现步骤

1. 依赖

引入 MySQL 和 Druid 的依赖

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.6</version>
</dependency>

2. 配置数据源

在 application.yml 文件中配置多个数据源。

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      datasource1:
        url: jdbc:mysql://localhost:3306/master_db
        username: root
        password: password
        initial-size: 5
        min-idle: 5
        max-active: 20
        max-wait: 60000
      datasource2:
        url: jdbc:mysql://localhost:3306/slave_db
        username: root
        password: password
        initial-size: 5
        min-idle: 5
        max-active: 20
        max-wait: 60000

3. 创建数据源配置类

创建一个配置类来定义数据源 Bean。

@Configuration
public class DataSourceConfig {

    @Bean
    @ConditionalOnProperty(prefix = "spring.datasource.druid", name = "datasource1")
    @ConfigurationProperties(prefix = "spring.datasource.druid.datasource1")
    public DataSource dataSource1() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConditionalOnProperty(prefix = "spring.datasource.druid", name = "datasource2")
    @ConfigurationProperties(prefix = "spring.datasource.druid.datasource2")
    public DataSource dataSource2() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setDefaultTargetDataSource(dataSource1());
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("dataSource1", dataSource1());
        targetDataSources.put("dataSource2", dataSource2());
        dynamicDataSource.setTargetDataSources(targetDataSources);
        return dynamicDataSource;
    }
}

4. 实现 AbstractRoutingDataSource

创建一个继承自 AbstractRoutingDataSource 的类,并实现 determineCurrentLookupKey 方法。

public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSource();
    }
}

5. 创建 DataSourceContextHolder

创建一个工具类来保存当前线程的数据源信息。

public class DataSourceContextHolder {

    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    public static void setDataSource(String dataSource) {
        CONTEXT_HOLDER.set(dataSource);
    }

    public static String getDataSource() {
        return CONTEXT_HOLDER.get();
    }

    public static void clearDataSource() {
        CONTEXT_HOLDER.remove();
    }
}

6. AOP动态切换数据源

使用 AOP 在方法执行前后切换数据源。

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class DataSourceAspect {
	// 方法或者类上的横切点
    @Pointcut("@annotation(dataSource) || @within(dataSource)")
    public void dataSourcePointcut(DataSource dataSource) {}

    @Before("dataSourcePointcut(dataSource)")
    public void switchDataSource(JoinPoint joinPoint, DataSource dataSource) {
        // 从注解中获取数据源标识
        String dataSourceKey = dataSource.value();
        // 切换到指定的数据源
        DataSourceContextHolder.setDataSourceType(dataSourceKey);
    }

    @AfterReturning(pointcut = "dataSourcePointcut(dataSource)", returning = "result")
    public void restoreDataSource(JoinPoint joinPoint, DataSource dataSource, Object result) {
        // 恢复默认数据源(可选)
        DataSourceContextHolder.clearDataSourceType();
    }
}

/**
* 自定义注解
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {

    String value() default "dataSource1";
}

7. 使用自定义注解

在需要切换数据源的方法上使用自定义注解。

注意

  • 数据源切换的逻辑应该尽可能简单和高效,以避免对应用程序性能产生负面影响。
  • 在切换数据源时,需要注意事务管理的问题,确保在同一个事务中只使用同一个数据源。

到此这篇关于SpringBoot中Druid连接池与多数据源切换的方法的文章就介绍到这了,更多相关SpringBoot Druid与数据源切换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot集成pf4j实现插件开发功能的代码示例

    SpringBoot集成pf4j实现插件开发功能的代码示例

    pf4j是一个插件框架,用于实现插件的动态加载,支持的插件格式(zip、jar),本文给大家介绍了SpringBoot集成pf4j实现插件开发功能的示例,文中通过代码示例给大家讲解的非常详细,需要的朋友可以参考下
    2024-07-07
  • Java_int、double型数组常用操作工具类(分享)

    Java_int、double型数组常用操作工具类(分享)

    下面小编就为大家带来一篇Java_int、double型数组常用操作工具类(分享)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • java递归与非递归实现扫描文件夹下所有文件

    java递归与非递归实现扫描文件夹下所有文件

    这篇文章主要为大家详细介绍了java递归与非递归实现扫描文件夹下所有文件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-02-02
  • java实现归并排序算法

    java实现归并排序算法

    归并排序:是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。 本文我们就来详细的探讨下。
    2015-04-04
  • Java enum的用法详细介绍及实例代码

    Java enum的用法详细介绍及实例代码

    这篇文章主要介绍了Java enum的用法详细介绍及实例代码的相关资料,需要的朋友可以参考下
    2017-02-02
  • idea下载svn的项目并且运行操作

    idea下载svn的项目并且运行操作

    这篇文章主要介绍了idea下载svn的项目并且运行操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • mybatis <foreach>标签动态增删改查方式

    mybatis <foreach>标签动态增删改查方式

    这篇文章主要介绍了mybatis <foreach>标签动态增删改查方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • java实现的海盗算法优化版

    java实现的海盗算法优化版

    这篇文章主要介绍了java实现的海盗算法优化版,结合实例形式分析了java海盗算法的具体实现技巧,需要的朋友可以参考下
    2017-07-07
  • java中optional的一些常用方法总结

    java中optional的一些常用方法总结

    Java8引入了一个非常强大的特性就是Optional类,其主要解决的问题就是我们编程时常常遇到的空指针异常,下面这篇文章主要给大家介绍了关于java中optional的一些常用方法,需要的朋友可以参考下
    2023-04-04
  • java中Scanner类的简单用法分享

    java中Scanner类的简单用法分享

    这篇文章主要介绍了java中Scanner类的简单用法,有需要的朋友可以参考一下
    2014-01-01

最新评论