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

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

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

其中 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

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
前端开发 Java 数据库连接
Spring Boot 3 整合 Mybatis-Plus 动态数据源实现多数据源切换
Spring Boot 3 整合 Mybatis-Plus 动态数据源实现多数据源切换
|
供应链 JavaScript 安全
SBOM概念和Java生成方式
SBOM概念和Java生成方式
|
运维 数据可视化 搜索推荐
低代码平台:10分钟从入门到原理
低代码平台:10分钟从入门到原理
1224 0
|
存储 前端开发 JavaScript
深入探索WebGIS地图开发技术
随着互联网技术的不断发展,WebGIS(Web地理信息系统)逐渐成为地理信息展示与分析的重要工具。WebGIS 系统能够通过浏览器展示地图,并支持地图操作(如缩放、平移、查询等),因此在导航、城市规划、环境监测、公共安全等领域有着广泛应用。
|
人工智能 Java 程序员
搭建AI智能体的Java神器:Google ADK深度解析
想用Java构建复杂的AI智能体?Google开源的ADK工具包来了!代码优先、模块化设计,让你像搭积木一样轻松组合智能体。从单体到多智能体系统,从简单工具到复杂编排,这篇文章带你玩转Java AI开发的全新境界。
1575 1
|
数据采集 监控 数据可视化
数据量暴涨时,抓取架构该如何应对?——豆瓣电影案例调研
本案例讲述了在豆瓣电影数据采集过程中,面对数据量激增和限制机制带来的挑战,如何通过引入爬虫代理、分布式架构与异步IO等技术手段,实现采集系统的优化与扩展,最终支撑起百万级请求的稳定抓取。
648 0
数据量暴涨时,抓取架构该如何应对?——豆瓣电影案例调研
|
存储 Java 数据库
Spring Boot中的动态数据源切换
Spring Boot中的动态数据源切换
|
存储 SQL 数据挖掘
深入理解 Flink 中的 State
Flink 的 State(状态)是其四大核心之一,为流处理和批处理任务提供强大支持。本文深入探讨 Flink 中的状态管理,涵盖 State 在 HDFS 中的存储格式、存在形式(如 ValueState、ListState 等)、使用方法、过期时间 TTL 和清除策略,并介绍 Table API 和 SQL 模块中的状态管理。通过实际案例,帮助读者理解如何在电商订单处理、实时日志统计等场景中有效利用状态管理功能。
1608 16
|
SQL 监控 druid
Druid数据库连接池的详细解析!分析说明数据库连接池Druid的参数配置和基本架构
本篇文章中介绍了数据库连接池Alibaba Druid的组成部分以及Druid的作用和基本配置。详细说明了Druid的基本架构,分别介绍了Druid中的实时节点,历史节点,查询节点,协调节点以及索引服务。通过本篇文章的学习,可以对数据库连接池Druid的参数配置和基本架构有清楚的认识。
3421 34
Druid数据库连接池的详细解析!分析说明数据库连接池Druid的参数配置和基本架构

热门文章

最新文章