3.3 Spring Boot多数据源动态切换:AbstractRoutingDataSource实战

Spring Boot多数据源动态切换:AbstractRoutingDataSource实战

引言

在企业级应用开发中,随着业务规模的扩大,我们经常会遇到需要同时操作多个数据库的场景:可能是主从读写分离、多租户架构、分库分表需求,或是需要同时连接业务库和日志库等特殊场景。传统的单数据源配置已无法满足这类需求,而Spring Boot提供的AbstractRoutingDataSource正是解决这类问题的利器。本文将深入探讨如何基于AbstractRoutingDataSource实现多数据源的动态切换,并通过实战代码演示具体实现方案。


一、核心原理剖析

1.1 AbstractRoutingDataSource工作机制

AbstractRoutingDataSource是Spring框架提供的一个抽象类,通过路由机制(Routing)实现数据源的动态切换。其核心逻辑是维护一个Map<Object, DataSource>结构,通过determineCurrentLookupKey()方法返回当前线程需要使用的数据源标识(lookup key),进而从目标数据源集合中获取对应的DataSource

1.2 线程级数据源管理

动态数据源切换的核心是线程隔离。每个请求线程通过ThreadLocal保存当前数据源标识,确保不同线程间的数据源选择互不干扰。这种设计完美适配Web应用的请求-响应模型,天然支持高并发场景。


二、实战:四步实现动态数据源

2.1 环境准备

pom.xml中添加基础依赖:

 

xml

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
</dependency>

2.2 数据源配置

application.yml中配置主从数据源:

 

yaml

datasource:master:driver-class-name: com.mysql.cj.jdbc.Driverjdbc-url: jdbc:mysql://localhost:3306/master_dbusername: rootpassword: master_pwdpool-name: MASTER_POOLslave:driver-class-name: com.mysql.cj.jdbc.Driverjdbc-url: jdbc:mysql://localhost:3306/slave_dbusername: rootpassword: slave_pwdpool-name: SLAVE_POOL

2.3 动态数据源核心实现

2.3.1 数据源上下文持有器
 

java

public class DataSourceContextHolder {private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();public static void setDataSource(String name) {CONTEXT.set(name);}public static String getDataSource() {return CONTEXT.get();}public static void clear() {CONTEXT.remove();}
}
2.3.2 动态数据源实现类
 

java

public class DynamicDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {return DataSourceContextHolder.getDataSource();}@Bean@ConfigurationProperties(prefix = "datasource.master")public DataSource masterDataSource() {return DataSourceBuilder.create().build();}@Bean@ConfigurationProperties(prefix = "datasource.slave")public DataSource slaveDataSource() {return DataSourceBuilder.create().build();}@Beanpublic DataSource dynamicDataSource() {Map<Object, Object> dataSources = new HashMap<>(2);dataSources.put("master", masterDataSource());dataSources.put("slave", slaveDataSource());DynamicDataSource ds = new DynamicDataSource();ds.setDefaultTargetDataSource(masterDataSource());ds.setTargetDataSources(dataSources);return ds;}
}

2.4 切换切面实现

 

java

@Aspect
@Component
public class DataSourceAspect {@Before("@annotation(com.example.annotation.Master)")public void switchMaster() {DataSourceContextHolder.setDataSource("master");}@Before("@annotation(com.example.annotation.Slave)")public void switchSlave() {DataSourceContextHolder.setDataSource("slave");}@AfterReturning("@annotation(com.example.annotation.Master) || @annotation(com.example.annotation.Slave)")public void clearDataSource() {DataSourceContextHolder.clear();}
}

三、进阶功能扩展

3.1 动态数据源注册

 

java

public void addNewDataSource(String key, DataSource dataSource) {Map<Object, Object> targetDataSources = new HashMap<>(dynamicDataSource.getTargetDataSources());targetDataSources.put(key, dataSource);dynamicDataSource.setTargetDataSources(targetDataSources);dynamicDataSource.afterPropertiesSet();
}

3.2 事务管理增强

 

java

@Bean
public PlatformTransactionManager transactionManager() {return new DataSourceTransactionManager(dynamicDataSource());
}

四、测试验证

 

java

@SpringBootTest
class DynamicDSTest {@Autowiredprivate JdbcTemplate jdbcTemplate;@Test@Mastervoid testMasterWrite() {jdbcTemplate.execute("INSERT INTO user(name) VALUES('test')");}@Test@Slavevoid testSlaveRead() {List<Map<String, Object>> result = jdbcTemplate.queryForList("SELECT * FROM user");System.out.println(result);}
}

五、注意事项与优化建议

  1. 连接池监控:建议集成Druid的监控功能,实时观察各数据源状态
  2. 失败回退机制:当目标数据源不可用时,自动切换到默认数据源
  3. 性能调优:根据实际场景调整各连接池参数(maxPoolSize、minIdle等)
  4. 分布式事务:对于跨数据源事务,建议使用Seata等分布式事务解决方案
  5. 动态刷新:结合Nacos配置中心实现数据源配置的热更新

总结

通过AbstractRoutingDataSource实现多数据源动态切换,我们既保持了Spring Boot简洁的配置风格,又获得了灵活的数据源管理能力。这种方案在中小型项目中表现优异,但对于需要复杂分片策略的大型分布式系统,建议考虑集成ShardingSphere等专业中间件。希望本文能为您在应对复杂数据源场景时提供有价值的参考。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/34330.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ios打包需要的证书及步骤

官网&#xff1a;https://developer.apple.com/account 避免他人登录apple账号的方法&#xff1a;就是让他们发测试设备的udid&#xff0c;手动注册到账号下&#xff0c;然后再给他们导p12证书和描述文件 iOS App Development iOS 开发版本签名&#xff08;仅限 iOS App&#x…

C#特性和反射

1。特性概念理解&#xff1f; 特性&#xff08;Attribute&#xff09;是用于在【运行时】传递程序中各种元素&#xff08;比如类、属性、方法、结构、枚举、组件等&#xff09;行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所…

【毕业论文格式】word分页符后的标题段前间距消失

文章目录 【问题描述】 分页符之后的段落开头&#xff0c;明明设置了标题有段前段后间距&#xff0c;但是没有显示间距&#xff1a; 【解决办法】 选中标题&#xff0c;选择边框 3. 选择段前间距&#xff0c;1~31磅的一个数 结果

操作系统-八股

进程基础&#xff1a; 进程定义&#xff1a;运行中的程序&#xff0c;有独立的内存空间和地址&#xff0c;是系统进行资源调度和分配的基本单位。 并发&#xff0c;并行 并发就是单核上面轮询&#xff0c;并行就是同时执行&#xff08;多核&#xff09;&#xff1b; 进程上下…

骑士74CMS_v3.34.0SE版uniapp全开源小程序怎么编译admin和member流程一篇文章说清楚

有粉丝一直问我骑士系统怎么编译后台和小程序目前骑士人才系统74CMS分标准版&#xff0c;创业板&#xff0c;专业版&#xff0c;其除功能不同外其配置方法完全一致有喜欢系统的也可以私信我或者找我获取 一.安装打包环境[Nodejs]这个就不用我说了吧&#xff0c;用不小于V20的版…

c语言zixue

该文主要是记录我学习中遇到的一些重点、易出问题的内容 教材p16.17 先从一个简单的例子开始吧 #include <stdio.h> //编译预处理命令 int main() //程序的主函数 {printf("To C"); //输出语句return 0; //返回语句 } #include <stdio.h>是编译预…

ollama API 本地调用

ollama API 本地调用 前提条件&#xff0c;ollama 已经启动了模型&#xff0c;查看 ollama 中的 model 名称 ollama list使用 openai 调用 from openai import OpenAI import openaiopenai.api_key ollama openai.base_url http://localhost:11434/v1/def get_completion(pro…

es6 尚硅谷 学习

1、let 1.变量不能重复声明 2.块级作用域 &#xff0c;只在块内有效 3.不存在变量提升&#xff0c;变量未声明之前不可使用 4.不影响作用域链 2、const const SCHOOL “温医”&#xff1b; 1.一定要有初始值 2.一般常量使用大写 3.常量不能赋值 4.块级作用域 5.对数组和对象…

在微信小程序或前端开发中,picker 和 select 都是用户交互中用于选择的组件,但它们在功能、设计和使用场景上有一定的区别

在微信小程序或前端开发中&#xff0c;picker 和 select 都是用户交互中用于选择的组件&#xff0c;但它们在功能、设计和使用场景上有一定的区别。 1. picker 的特点 描述&#xff1a; picker 是微信小程序中的原生组件&#xff0c;通常用于选择单项或多项值&#xff0c;如时…

C#通过API接口返回流式响应内容---分块编码方式

1、背景 上一篇文章《C#通过API接口返回流式响应内容—SSE方式》阐述了通过SSE&#xff08;Server Send Event&#xff09;方式&#xff0c;由服务器端推送数据到浏览器。本篇是通过分块编码的方式实现 2、效果 3、具体代码 3.1 API端实现 [HttpGet] public async Task Chu…

【SpringMVC】入门版

1.基本概念 1.1三层架构 三层架构也就是我们常说的b/s架构中的表现层&#xff0c;业务层和持久层,每层都各司其职&#xff0c;下面来分别讲解这三层的作用。 表现层&#xff1a; 也就是我们常说的web层。它负责接收客户端的请求&#xff0c;向客户端响应结果&#xff0c;通…

各省水资源平台 水资源遥测终端机都用什么协议

各个省水资源平台 水资源遥测终端机 的建设大部分从2012年开始启动&#xff0c;经过多年建设&#xff0c;基本都已经形成了稳定的通讯要求&#xff1b;河北瑾航科技 遥测终端机&#xff0c;兼容了大部分省市的通讯协议&#xff0c;如果需要&#xff0c;可以咨询和互相学习&…

【Android】RuntimeShader 应用

1 简介 RuntimeShader 是 Android 13&#xff08;T&#xff09;中新增的特性&#xff0c;用于逐像素渲染界面&#xff0c;它使用 AGSL&#xff08;Android Graphics Shading Language&#xff09;编写着色器代码&#xff0c;底层基于 Skia 图形渲染引擎。官方介绍详见 → Runti…

ShenNiusModularity项目源码学习(16:ShenNius.Admin.Mvc项目分析-1)

ShenNius.Admin.Mvc项目是MVC模式的启动项目&#xff0c;包括了MVC模式下所需的所有的页面、控制器类、资源、js文件等数据&#xff0c;该项目仅依赖ShenNius.Admin.API项目&#xff0c;主要使用后者的ShenniusAdminApiModule模块类及一些依赖项目中定义的辅助类等。本文学习Sh…

maven wrapper的使用

写在前面 考虑这样的场景&#xff0c;张三创建了一个maven项目使用了3.9版本&#xff0c;当李四下载下来去开发配置的却是3.6版本&#xff0c;此时李四就不得不再去配置一个3.9版本的maven&#xff0c;为了解决这个问题&#xff0c;maven引入了maven wrapper的机制&#xff08…

1、操作系统引论

一、操作系统 会使用linux系统 建议大家先学会linux的基础指令&#xff0c;可以看菜鸟教程网站进行学习。 1、各种定义 操作系统定义 管理计算机的 硬件 和软件资源&#xff0c; 能对各类作业进行调度&#xff0c;方便用户使用计算机的程序集合。操作系统运行在内核态&#xf…

如何用正则表达式爬取古诗文网中的数据(python爬虫)

一、了解正则表达式的基本内容&#xff1a; 什么是正则表达式 正则表达式&#xff08;Regular Expression&#xff0c;简称 regex&#xff09;是一种用于匹配字符串的模式。它通过特定的语法规则&#xff0c;可以高效地搜索、替换和提取文本中的特定内容。正则表达式广泛应用于…

网络空间安全(33)MSF漏洞利用

前言 Metasploit Framework&#xff08;简称MSF&#xff09;是一款功能强大的开源安全漏洞利用和测试工具&#xff0c;广泛应用于渗透测试中。MSF提供了丰富的漏洞利用模块&#xff0c;允许安全研究人员和渗透测试人员利用目标系统中的已知漏洞进行攻击。 一、漏洞利用模块&…

PBS 脚本及 运行

PBS 脚本命令的调度 PBS 脚本运行命令**如何跑&#xff1f;**准备 PBS 脚本&#xff1f; 成品 本文涉及&#xff1a; PBS 命令 Shell 命令 Python 命令 使用命令行运行作业&#xff0c;需要在 HPC 中放好 PBS 脚本。 如何写一个 PBS 脚本&#xff0c;下面以自己的 PBS 脚本为例…

Spring Cloud LoadBalancer 原理与实践

背景 当前我们的微服务架构基于Spring Cloud Alibaba体系&#xff0c;通过定制NacosRule实现了跨集群访问和灰度发布功能。但随着Spring Cloud与Nacos版本升级&#xff0c;官方已弃用Ribbon转向LoadBalancer&#xff0c;这要求我们完成以下技术升级&#xff1a; 负载均衡机制…