仿写@DS 多数据源动态切换

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 最近公司在做项目,用到了多数据源,我在网上找了好多的开源项目。

最近公司在做项目,用到了多数据源,我在网上找了好多的开源项目。

其中 https://github.com/baomidou/dynamic-datasource-spring-boot-starter 这个挺不错的。 内容丰富,完善。

我把代码clone 下来了自己仿写了一个乞丐版的。 但是主要功能还是有的。

1, 先按照标准的yml 格式 定义两个配置类

@Component
@ConfigurationProperties(prefix = DynamicDataSourceProperties.PREFIX)
public class DynamicDataSourceProperties {

public static final String PREFIX = "spring.datasource.dynamic";
/**
 * 必须设置默认的库,默认master
 */
private String primary = "master";

/**
 * 每一个数据源
 */
private Map<String, DataSourceProperty> datasource = new LinkedHashMap<>();

private Map<Object, Object> dataSourceMap = new LinkedHashMap<>();

// get set 省略

 // 初始化构造数据源集合
@PostConstruct
public void initDataSource(){
    for(Map.Entry<String, DataSourceProperty> key:this.datasource.entrySet()) {
        DataSourceProperty property = key.getValue();
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(property.getDriverClassName());
        druidDataSource.setUrl(property.getUrl());
        druidDataSource.setUsername(property.getUsername());
        druidDataSource.setPassword(property.getPassword());
        dataSourceMap.put(key.getKey(),druidDataSource);
    }
}

// 通过路由切换,动态绑定数据源

@Bean
public DataSource dynamicDataSource() {
    DynamicDataSource dynamicDataSource = new DynamicDataSource();
    
    // 设置主数据源
    dynamicDataSource.setTargetDataSources(getDataSourceMap());
    
    // 设置数据源集合
    dynamicDataSource.setDefaultTargetDataSource(getDataSourceMap().get(primary));
    return dynamicDataSource;
}

}

public class DataSourceProperty {

/**
 * JDBC driver
 */
private String driverClassName;
/**
 * JDBC url 地址
 */
private String url;
/**
 * JDBC 用户名
 */
private String username;
/**
 * JDBC 密码
 */
private String password;

// get set  省略
}

在创建路由切换的类:

public class DynamicDataSource extends AbstractRoutingDataSource {

//默认支持两种, dataSource 和String  , key 就是上面代码set 进去Map 的key 
// 发现当前数据源 
@Override
protected Object determineCurrentLookupKey() {
    return DynamicDataSourceContextHolder.peek();
}

}

数据源设置类:

/**

  • 核心基于ThreadLocal的切换数据源工具类

*

  • @author TaoYu Kanyuxia
  • @since 1.0.0

*/
public final class DynamicDataSourceContextHolder {

/**
 * 为什么要用链表存储(准确的是栈)
 * <pre>
 * 为了支持嵌套切换,如ABC三个service都是不同的数据源
 * 其中A的某个业务要调B的方法,B的方法需要调用C的方法。一级一级调用切换,形成了链。
 * 传统的只设置当前线程的方式不能满足此业务需求,必须使用栈,后进先出。
 * </pre>
 */
private static final ThreadLocal<Deque<String>> LOOKUP_KEY_HOLDER = new NamedThreadLocal<Deque<String>>("dynamic-datasource") {
    @Override
    protected Deque<String> initialValue() {
        return new ArrayDeque<>();
    }
};

private DynamicDataSourceContextHolder() {
}

/**
 * 获得当前线程数据源
 *
 * @return 数据源名称
 */
public static String peek() {
    return LOOKUP_KEY_HOLDER.get().peek();
}

/**
 * 设置当前线程数据源
 * <p>
 * 如非必要不要手动调用,调用后确保最终清除
 * </p>
 *
 * @param ds 数据源名称
 */
public static String push(String ds) {
    String dataSourceStr = StringUtils.isEmpty(ds) ? "" : ds;
    LOOKUP_KEY_HOLDER.get().push(dataSourceStr);
    return dataSourceStr;
}

/**
 * 清空当前线程数据源
 * <p>
 * 如果当前线程是连续切换数据源 只会移除掉当前线程的数据源名称
 * </p>
 */
public static void poll() {
    Deque<String> deque = LOOKUP_KEY_HOLDER.get();
    deque.poll();
    if (deque.isEmpty()) {
        LOOKUP_KEY_HOLDER.remove();
    }
}

/**
 * 强制清空本地线程
 * <p>
 * 防止内存泄漏,如手动调用了push可调用此方法确保清除
 * </p>
 */
public static void clear() {
    LOOKUP_KEY_HOLDER.remove();
}

自定义注解,可以作用在类和方法上面, 数据源嵌套, 先进后出, 使用最深层的注解数据源。

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DS {

String value() default "master";

}

AOP 实现

@Component
@Aspect
@Order(-1) // 优先级最高
public class AspectDs {

@Pointcut("@within(xxxxxx.DS)||@annotation(xxxxxx.DS)")
public void ds() {

}

@Before(value = "ds()")
public void dataSource(JoinPoint joinPoint) {
    Class cls = joinPoint.getSignature().getDeclaringType();
    // 如果类上面有就使用 类上面的
    DS ds = AnnotationUtils.findAnnotation(cls, DS.class);
    if(ds != null && !"".equals(ds)) {
        DynamicDataSourceContextHolder.push(ds.value());
        System.out.println( cls.getSimpleName() + "类上面的 动态 数据源 :"+ds.value());
    }
    MethodSignature signature = (MethodSignature)joinPoint.getSignature();
    Method method = signature.getMethod();
    DS annotation = AnnotationUtils.findAnnotation(method, DS.class);
    if(annotation != null && !"".equals(annotation)){
        DynamicDataSourceContextHolder.push(ds.value());
        System.out.println(method.getName()+ "方法上面的动态数据源 :"+ds.value());
    }
}

}

application.yml 配置

spring:
datasource:

dynamic:
  primary: master #设置默认的数据源或者数据源组,默认值即为master
  datasource:
    master:
      url: jdbc:mysql://xxxxxxxxxxx:3306/db1
      username: root
      password: xxxxxxxxx
      driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
    slave_1:
      url: jdbc:mysql://xxxxxxxxx:3306/db2
      username: root
      password: xxxxxxxxxxxxxxxx
      driver-class-name: com.mysql.jdbc.Driver

测试:10.png

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
域名解析 Kubernetes Java
图文详述Nacos配置中心使用:应用间配置共享、扩展配置文件加载优先级、新老版本差异
图文详述Nacos配置中心使用:应用间配置共享、扩展配置文件加载优先级、新老版本差异
4459 1
图文详述Nacos配置中心使用:应用间配置共享、扩展配置文件加载优先级、新老版本差异
|
3月前
|
监控 Java 数据库
SpringBoot 实现动态切换数据源:优雅之道
在微服务架构和复杂的应用系统中,随着业务规模的扩大,单个数据源往往无法满足多样化的数据访问需求。这时,动态切换数据源成为了一个重要的技术需求,它允许应用程序在运行时根据业务逻辑或用户请求,灵活地选择不同的数据源进行操作。在Spring Boot框架中,实现数据源的动态切换既是一项挑战,也是一项展现技术优雅性的机会。以下,我们将深入探讨如何在Spring Boot中实现数据源的动态切换,并追求实现的优雅性。
259 1
|
存储 Java 数据库连接
Mybatis-plus@DS实现动态切换数据源应用
Mybatis-plus@DS实现动态切换数据源应用
661 0
|
7月前
|
小程序 数据格式
【经验分享】如何实现自定义数据源的级联选择组件?
【经验分享】如何实现自定义数据源的级联选择组件?
88 6
|
7月前
|
移动开发 前端开发 JavaScript
动态获取新增的数据+项目实例介绍
动态获取新增的数据+项目实例介绍
95 0
|
7月前
|
存储 Java 关系型数据库
springboot整合多数据源的配置以及动态切换数据源,注解切换数据源
springboot整合多数据源的配置以及动态切换数据源,注解切换数据源
699 0
|
Java 数据库 开发者
自动配置要点解读
自动配置要点解读
|
Java 数据库
SSM 最简单的实现操作 多数据源&动态切换
SSM 最简单的实现操作 多数据源&动态切换
282 0
SSM 最简单的实现操作 多数据源&动态切换
|
存储 缓存 druid
基于springboot+jpa 实现多租户动态切换多数据源 - 基于dynamic-datasource实现多租户动态切换数据源
基于springboot+jpa 实现多租户动态切换多数据源 - 基于dynamic-datasource实现多租户动态切换数据源
3299 0
基于springboot+jpa 实现多租户动态切换多数据源 - 基于dynamic-datasource实现多租户动态切换数据源
|
SQL druid Java
基于springboot+jpa 实现多租户动态切换多数据源 - 使用Flyway实现多数据源数据库脚本管理和迭代更新
基于springboot+jpa 实现多租户动态切换多数据源 - 使用Flyway实现多数据源数据库脚本管理和迭代更新
1676 0
基于springboot+jpa 实现多租户动态切换多数据源 - 使用Flyway实现多数据源数据库脚本管理和迭代更新