0


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

** ❃博主首页 :**
「码到三十五」
,同名公众号 :「码到三十五」,wx号 : 「liwu0213」
☠博主专栏 :
<源码解读>

<面试攻关>

♝博主的话 :搬的每块砖,皆为峰峦之基;公众号搜索「码到三十五」关注这个爱发技术干货的coder,一起筑基


微服务架构中多数据源切换是个常见的需求。Spring Boot 提供了强大的支持来简化这一过程.

多数据源切换原理

多数据源切换的原理主要基于 Spring 的

AbstractRoutingDataSource

类。

AbstractRoutingDataSource

类允许根据运行时上下文动态选择数据源。其核心在于实现

determineCurrentLookupKey

方法,该方法决定当前操作使用哪个数据源。

AbstractRoutingDataSource

实现多数据源切换的原理:

1. 数据源映射
  • AbstractRoutingDataSource 内部维护了一个映射(Map),用于存储数据源标识(key)和对应的数据源实例(value)。这个映射允许根据数据源标识快速查找和获取对应的数据源。
2. 数据源标识的确定
  • AbstractRoutingDataSource 提供了一个抽象方法 determineCurrentLookupKey(),该方法用于确定当前需要使用的数据源标识。这个方法需要由子类实现,以返回当前线程或请求应该使用的数据源标识。
3. 数据源的选择与连接获取
  • 当应用程序需要获取数据库连接时,AbstractRoutingDataSourcegetConnection() 方法会被调用。这个方法首先调用 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:5min-idle:5max-active:20max-wait:60000datasource2:url: jdbc:mysql://localhost:3306/slave_db
        username: root
        password: password
        initial-size:5min-idle:5max-active:20max-wait:60000
3. 创建数据源配置类

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

@ConfigurationpublicclassDataSourceConfig{@Bean@ConditionalOnProperty(prefix ="spring.datasource.druid", name ="datasource1")@ConfigurationProperties(prefix ="spring.datasource.druid.datasource1")publicDataSourcedataSource1(){returnDruidDataSourceBuilder.create().build();}@Bean@ConditionalOnProperty(prefix ="spring.datasource.druid", name ="datasource2")@ConfigurationProperties(prefix ="spring.datasource.druid.datasource2")publicDataSourcedataSource2(){returnDruidDataSourceBuilder.create().build();}@Bean@PrimarypublicDataSourcedynamicDataSource(){DynamicDataSource dynamicDataSource =newDynamicDataSource();
        dynamicDataSource.setDefaultTargetDataSource(dataSource1());Map<Object,Object> targetDataSources =newHashMap<>();
        targetDataSources.put("dataSource1",dataSource1());
        targetDataSources.put("dataSource2",dataSource2());
        dynamicDataSource.setTargetDataSources(targetDataSources);return dynamicDataSource;}}
4. 实现
AbstractRoutingDataSource

创建一个继承自

AbstractRoutingDataSource

的类,并实现

determineCurrentLookupKey

方法。

publicclassDynamicDataSourceextendsAbstractRoutingDataSource{@OverrideprotectedObjectdetermineCurrentLookupKey(){returnDataSourceContextHolder.getDataSource();}}
5. 创建
DataSourceContextHolder

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

publicclassDataSourceContextHolder{privatestaticfinalThreadLocal<String> CONTEXT_HOLDER =newThreadLocal<>();publicstaticvoidsetDataSource(String dataSource){
        CONTEXT_HOLDER.set(dataSource);}publicstaticStringgetDataSource(){return CONTEXT_HOLDER.get();}publicstaticvoidclearDataSource(){
        CONTEXT_HOLDER.remove();}}
6. AOP动态切换数据源

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

importorg.aspectj.lang.JoinPoint;importorg.aspectj.lang.annotation.AfterReturning;importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.annotation.Before;importorg.aspectj.lang.annotation.Pointcut;importorg.springframework.stereotype.Component;@Aspect@ComponentpublicclassDataSourceAspect{// 方法或者类上的横切点@Pointcut("@annotation(dataSource) || @within(dataSource)")publicvoiddataSourcePointcut(DataSource dataSource){}@Before("dataSourcePointcut(dataSource)")publicvoidswitchDataSource(JoinPoint joinPoint,DataSource dataSource){// 从注解中获取数据源标识String dataSourceKey = dataSource.value();// 切换到指定的数据源DataSourceContextHolder.setDataSourceType(dataSourceKey);}@AfterReturning(pointcut ="dataSourcePointcut(dataSource)", returning ="result")publicvoidrestoreDataSource(JoinPoint joinPoint,DataSource dataSource,Object result){// 恢复默认数据源(可选)DataSourceContextHolder.clearDataSourceType();}}/**
* 自定义注解
*/@Target({ElementType.METHOD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public@interfaceDataSource{Stringvalue()default"dataSource1";}
7. 使用自定义注解

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

@ServicepublicclassUserService{@AutowiredprivateUserRepository userRepo;@DataSource("dataSource2")publicvoidqueryAndUpdate(List<Integer> deptIds){List<User> userList = userRepo.findAll();System.out.println(userList.size());// 其他操作}}

注意

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

关注公众号[码到三十五]获取更多技术干货 !


本文转载自: https://blog.csdn.net/qq_26664043/article/details/143779354
版权归原作者 码到三十五 所有, 如有侵权,请联系我们删除。

“Spring Boot 中 Druid 连接池与多数据源切换的方法”的评论:

还没有评论