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与数据源切换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • JavaEE idea的smart tomcat插件使用

    JavaEE idea的smart tomcat插件使用

    这篇文章主要介绍了JavaEE idea的smart tomcat插件使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-09-09
  • Maven依赖管理的用法介绍

    Maven依赖管理的用法介绍

    依赖管理是项目管理中非常重要的一环。几乎任何项目开发的时候需要都需要使用到库。而这些库很可能又依赖别的库,这样整个项目的依赖形成了一个树状结构,而随着这个依赖的树的延伸和扩大,一系列问题就会随之产生
    2022-08-08
  • Java环境配置图文教程(推荐)

    Java环境配置图文教程(推荐)

    下面小编就为大家带来一篇Java环境配置图文教程(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • Java方法递归的形式和常见递归算法(方法递归结合File类查找文件)

    Java方法递归的形式和常见递归算法(方法递归结合File类查找文件)

    方法递归方法直接调用自己或者间接调用自己的形式称为方法递归( recursion),递归做为一种算法在程序设计语言中广泛应用,这篇文章主要介绍了Java方法递归的形式和常见递归算法-方法递归结合File类查找文件,需要的朋友可以参考下
    2023-02-02
  • mybatis 实现多条update同时执行

    mybatis 实现多条update同时执行

    这篇文章主要介绍了mybatis 实现多条update同时执行,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01
  • springboot实现全局异常捕获的使用示例

    springboot实现全局异常捕获的使用示例

    任何系统,我们不会傻傻的在每一个地方进行异常捕获和处理,整个系统一般我们会在一个的地方统一进行异常处理,本文主要介绍了springboot实现全局异常捕获的使用示例,感兴趣的可以了解一下
    2023-11-11
  • SpringBoot实现加载yml文件中字典数据

    SpringBoot实现加载yml文件中字典数据

    这篇文章主要为大家详细介绍了SpringBoot如何实现加载yml文件中字典数据,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起了解一下
    2023-04-04
  • Springboot中的异步任务执行及监控详解

    Springboot中的异步任务执行及监控详解

    这篇文章主要介绍了Springboot中的异步任务执行及监控详解,除了自己实现线程外,springboot本身就提供了通过注解的方式,进行异步任务的执行,下面主要记录一下,在Springboot项目中实现异步任务,以及对异步任务进行封装监控,需要的朋友可以参考下
    2023-10-10
  • jsp页面中获取servlet请求中的参数的办法详解

    jsp页面中获取servlet请求中的参数的办法详解

    在JAVA WEB应用中,如何获取servlet请求中的参数,本文讲解了jsp页面中获取servlet请求中的参数的办法
    2018-03-03
  • Spring的事务管理你了解吗

    Spring的事务管理你了解吗

    这篇文章主要为大家介绍了Spring的事务管理,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-01-01

最新评论