SpringBoot多数据源最佳实践

为什么需要spring boot多数据源
最常见的场景就是单体架构系统需要跨库进行业务增删改查,例如一个购车系统可能需要查询用户信息,然后在进行购买汽车的逻辑。而用户表和汽车表可能不在一个数据库中。

如下图所示,可能一个购买汽车的下单流程为:

用户提交请求。
基于id到数据源1查询当前用户信息。
基于汽车id到数据源3查询汽车信息。
将用户id、汽车id、数量存到数据源2订单表中。

面对这种数据源,我们不妨了解一下spring数据源加载原理,寻找可以扩展的点从而完成多数据源切换完成该业务需求的开发。

了解spring数据源加载原理
我们在调试spring数据源的时候看到这么一个类AbstractRoutingDataSource的类,它的类图如下所示:

数据源加载整体流程解析
可以看到它用到了InitializingBean这个接口,说明在bean加载完成之后肯定有进行一些相关数据源的操作,我们不妨看看源码。

我们在AbstractRoutingDataSource看到这个方法的实现,如下所示,可以看到它的逻辑很简单:

从targetDataSources获取到数据源的key的value存到resolvedDataSources,作为后续数据源切换时用到的材料。
如果resolvedDefaultDataSource 不为空,则将当前项目的defaultTargetDataSource 设置为defaultTargetDataSource 。

@Overridepublic void afterPropertiesSet() {if (this.targetDataSources == null) {throw new IllegalArgumentException("Property 'targetDataSources' is required");}//将targetDataSources的值存到resolvedDataSources中,作为后续切换的依据。this.resolvedDataSources = CollectionUtils.newHashMap(this.targetDataSources.size());this.targetDataSources.forEach((key, value) -> {Object lookupKey = resolveSpecifiedLookupKey(key);DataSource dataSource = resolveSpecifiedDataSource(value);this.resolvedDataSources.put(lookupKey, dataSource);});
//如果resolvedDefaultDataSource 不为空,则将当前项目的defaultTargetDataSource 设置为defaultTargetDataSourceif (this.defaultTargetDataSource != null) {this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);}}

了解spring boot启动时数据源的基本流程之后,我们再来了解一下这个加载的细节。

遍历targetDataSources时,对key和value解析流程
上面整体流程时提到,spring会从targetDataSources中取出数据源的key和value进行解析然后存放到resolvedDataSources,这里我们不妨看看实现细节。

首先是resolveSpecifiedLookupKey方法,源码如下,可以看到代码实现很简单,原原本本返回出去即可。

protected Object resolveSpecifiedLookupKey(Object lookupKey) {return lookupKey;}

再来看看resolveSpecifiedDataSource,逻辑也很简单,如果传进来的数据源配置是字符串类型,说明是配置中取到的,需要用dataSourceLookup转换成数据源类,如果本身就是DataSource类直接返回即可。

protected DataSource resolveSpecifiedDataSource(Object dataSource) throws IllegalArgumentException {if (dataSource instanceof DataSource) {return (DataSource) dataSource;}else if (dataSource instanceof String) {return this.dataSourceLookup.getDataSource((String) dataSource);}else {throw new IllegalArgumentException("Illegal data source value - only [javax.sql.DataSource] and String supported: " + dataSource);}}
spring何时决定使用哪个数据源

这里我们不妨随便拿一段mybatis查询的业务代码来debug了解一下细节,以笔者为例笔者就在下面这段代码中插入一个断点。

然后在debug过程中走到了一个DataSourceUtils工具类,调用一个getConnection方法,可以看出这个操作就是和数据源相关的。

我们步入查看逻辑,于是我们的代码又来到了AbstractRoutingDataSource,可以看到一个determineTargetDataSource方法,我们猜想这个可能就和数据源切换有关系。

重点来了,笔者这里将代码贴出来,可以看到determineTargetDataSource的逻辑:

获取当前web应用用到key。
拿着key到上文启动时存放数据源键值对的resolvedDataSources获取数据源。
返回数据源出去,然后代码会调用getConnection建立连接。

protected DataSource determineTargetDataSource() {Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");Object lookupKey = determineCurrentLookupKey();DataSource dataSource = this.resolvedDataSources.get(lookupKey);if (dataSource == null && (this.lenientFallback || lookupKey == null)) {dataSource = this.resolvedDefaultDataSource;}if (dataSource == null) {throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");}return dataSource;}

我们查看代码细节,可以看到determineCurrentLookupKey是一个抽象类,默认情况下有个单数据源的实现类,所以如果我们希望动态切换数据源,完完全全可以继承这个类,然后实现动态数据源切换逻辑,从而实现spring多数据源动态切换,一个功能在多个数据源中查询的逻辑。

功能实现思路
从源码中了解了spring的设计思路之后,我们现在就不妨设计一下多数据源切换的实现思路。首先是技术实现上:

maven引入相关依赖。
编写多数据源的配置。
编写配置类将数据源加载到spring容器中。
编写一个线程数据源管理类,分别存放每一个请求线程的数据源key值。
编写一个数据源管理类,负责加载项目运行时的数据源加载和存放。
继承AbstractRoutingDataSource重写determineCurrentLookupKey基于线程数据源管理类实现获取最新数据源的逻辑。


业务实现上:

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

【InternLM 实战营笔记】浦语·灵笔的图文理解及创作部署、 Lagent 工具调用 Demo

浦语灵笔的图文理解及创作部署 浦语灵笔是基于书生浦语大语言模型研发的视觉-语言大模型,提供出色的图文理解和创作能力,结合了视觉和语言的先进技术,能够实现图像到文本、文本到图像的双向转换。使用浦语灵笔大模型可以轻松的创作一篇图文推…

mybatis原理图,我拿到了梦寐以求的字节跳动和腾讯双offer

Kafka 如何做到支持百万级 TPS ? 先用一张思维导图直接告诉你答案: 顺序读写磁盘 生产者写入数据和消费者读取数据都是顺序读写的,先来一张图直观感受一下顺序读写和随机读写的速度: 从图中可以看出传统硬盘或者SSD的顺序读写甚…

MySQL 多表查询 连接查询 外连接

介绍 MySQL 多表查询 连接查询 内连接 外连接分为两种,左外和右外连接, 左外:相当于查询表1(左表)的所有数据 包含 表1和表2交集部分的数据,完全包含左表的数据 右外:相当于查询表2(右表)的所有数据 包含 表1和表2交集部分的数据…

攻防世界例题wp

1.看到_wakeup()函数第一反应要么触发,要么绕过在这里绕过 2.构造payload实例化一个对象后反序列化 3构造脚本如下: 4.因为它是一个绕过的方法所以我们要使用绕过的方法。 5.继续构造payload将上图的1换成2进行绕过 最终的payload为 O:4:"xctf…

消息队列+更新DB极易引发的DB并发修改bug

背景 我们在生产系统中和其他系统进行交互时一般都会通过消息队列来解耦生产者和消费者,然后通过每个使用方消费消息队列的消息的方式来完成消息的消费,并且一般来说我们消费消息后极有可能会操作DB,不过这种方式如果处理不够仔细&#xff0…

[攻防世界]-Web:fileclude解析

查看网页 代码审计: file_get_contents($file2):读取文件内容并将内容返回 解法一payload: ?file1php://filter/readconvert.base64-encode/resourceflag.php&file2data://text/plain,hello%20ctf 解法二payload:

AI新工具(20240229) Ideogram 1.0先进的文本转图像模型发布;search2ai让 LLM API 支持联网搜索

1: LTX Studio LTX Studio开放测试,用户可以通过输入文本来生成超过25秒的微电影视频 LTX Studio是由著名AI平台Lightricks推出的生成式AI电影制作平台。用户可以通过输入文本来生成超过25秒的微电影视频,并且可以对视频的镜头切换、角色、场景一致性、…

C++的晨曦之旅:开启编程的新篇章

个人主页:日刷百题 系列专栏:〖C/C小游戏〗〖Linux〗〖数据结构〗 〖C语言〗 🌎欢迎各位→点赞👍收藏⭐️留言📝 ​ ​ 一、 命名空间 在 C/C 中,变量、函数和后面要学到的类都是大量存在的&#xff0…

继电保护测试仪

武汉凯迪正大继电保护测试仪主要特点 1.满足现场试验要求。本仪器具有标准的四相电压,三相电流输出,既可对传统的各种继电器及保护装置进行试验,也可对现代各种微机保护进行各种试验,特别是对变压器差功保护和备自投装…

南方电网的能源棋局上,蔚来换电扮演什么角色?

2 月 26 日,南网储能科技与蔚来能源签署协议,将充换电站、储能站、可调负载等聚合资源连接到虚拟电厂平台,推动换电站作为分布式储能在虚拟电厂项目上的应用。 蔚来换电站是国内首个智慧微电网型分布式换电设施,可透过换电订单预…

【递归搜索回溯专栏】前言与本专栏介绍

本专栏内容为:递归,搜索与回溯算法专栏。 通过本专栏的深入学习,你可以了解并掌握算法。 💓博主csdn个人主页:小小unicorn ⏩专栏分类:递归搜索回溯专栏 🚚代码仓库:小小unicorn的代…

【YOLO v5 v7 v8 小目标改进】ODConv:在卷积核所有维度(数量、空间、输入、输出)上应用注意力机制来优化传统动态卷积

ODConv:在卷积核所有维度(数量、空间、输入、输出)上应用注意力机制来优化传统的动态卷积 提出背景传统动态卷积全维动态卷积效果 小目标涨点YOLO v5 魔改YOLO v7 魔改YOLO v8 魔改 论文:https://openreview.net/pdf?idDmpCfq6Mg…

LeetCode54题:螺旋矩阵(python3)

路径的长度即为矩阵中的元素数量,当路径的长度达到矩阵中的元素数量时即为完整路径,将该路径返回。 循环打印: “从左向右、从上向下、从右向左、从下向上” 四个方向循环打印。 class Solution:def spiralOrder(self, matrix: List[List[i…

计算机网络_2.2物理层下面的传输媒体

2.2物理层下面的传输媒体 一、传输媒体的分类二、导向型传输媒体1、同轴电缆2、双绞线3、光纤(1)光纤通信原理(2)光纤组成(4)多模光纤与单模光纤对比(5)光纤的波长与规格&#xff08…

AttributeError: ‘list‘ object has no attribute ‘view‘

问题描述 训练yolov9的时候遇到了下面的问题。 In loss_tal.py: pred_distri, pred_scores torch.cat([xi.view(feats[0].shape[0], self.no, -1) for xi in feats], 2).split( (self.reg_max * 4, self.nc), 1) The error is as follows: AttributeError: list …

NLP - 神经网络与反向传播

使用神经网络进行命名实体识别(二值词窗分类) 根据上下文窗口 建立词向量 通过一个神经网络层,通过一个逻辑分类器,得到这个概率是属于特定实体词的预测概率。 另一个分类器来比较说明 这个词是哪个实体类型(比较概率…

赵本山与高秀敏夫妇本想找范伟要那1200元电视机垫款,却不好意思向范伟开口--小品《面子》(中1)的台词

赵本山与高秀敏夫妇本想找范伟要那1200元电视机垫款,却不好意思向范伟开口 --小品《面子》(中1)的台词 表演者:赵本山 高秀敏 范伟 (接上) 高秀敏:咱俩抓紧提事啊 赵本山:不着急…

Python 迭代器和生成器的妙用

本文将探讨python的迭代器和生成器在实际场景中的一些巧妙用法。掌握迭代器和生成器的使用,能够让开发者在解决实际问题时更加得心应手。 Python 迭代器的妙用 Python 的迭代器是一个实现了迭代器协议的对象,它包含方法 __iter__() 和 __next__()。迭代…

实现任意系统下载office文件的域控

一.背景 最近用户提出需求:某个系统A下载的excel文档需要进行权限控制,比如只能下载文档的用户(即文档owner)查看或者编辑,其他人想要查看或者编辑,需要文档owner进行手动设置,当然也可以手动取…

初识C语言—指针

.h 头文件(函数的声明,类型的声明,头文件的包含) .c 源文件(函数实现) 浮点数的四舍五入,不能用你肉眼看到的数值来计算,因为浮点数在内存中有可能不能精确保存。 内存&#xff1a…