MybatisPlus 多数据源 @DS 选择深入源码理解原理

文章目录

  • MybatisPlus 多数据源 @DS 选择深入源码理解原理

MybatisPlus 多数据源 @DS 选择深入源码理解原理

数据源的选择,拦截器为DynamicDataSourceAnnotationInterceptor

image-20240710134040796

这里利用了一个MethodInterceptor接口,我们看看,我们可以看到这个包是org.aopalliance.intercept通过搜索得知

image-20240710134135389

aopalliance是对AOP和Java有浓厚兴趣的软件开发人员联合成立的开源项目,Spring是按照AOP联盟的规范做的实现,可见Spring是一个集众多基础框架于一身的伟大软件。aopalliance包里面只有接口,没有任何实现,这就是一个规范定义。

下面借用了一张图,spring aop实现了aopalliance的接口

img

现在我们知道了这个MethodInterceptor会拦截方法,类似于apesctJ里的around环绕通知。我们看看mybatisplus的拦截器是怎么处理的

image-20240710135704171

核心代码就这三行,在方法执行前先确定数据源推入到DynamicDataSourceContextHolder,然后再执行方法,再poll出用过的这个数据源,这个玩意是用来持有当前线程要用哪个数据源的。废话不多说直接看源码

image-20240710135901474

这个玩意很简单,就是用来持有当前线程调每个方法时要用哪个数据源,在方法执行之前把这个方法要用的数据源字符串压入栈,执行完弹出。在数据保存的时候他用的Deque,我们看到初始化的时候用的new ArrayDeque<>(),顺便看看这个ArrayDeque底层,其实就是一个对象数组、队列头、尾,默认数组长度16

image-20240710140055287

那么再看下它进入方法之前是怎么获取数据源字符串的,determineDatasource方法

image-20240710140355285

这个方法也很简单,获取当前被执行方法,如果这个方法上有DS注解,那就用这个注解,没有的话再去看这个方法所在类上有没有DS注解,有的话就用这个注解。有了注解之后还做了个判定是不是动态数据源表达式(DYNAMIC_PREFIX开头的,就是#开头的),如果是用了动态表达式的再执行动态表达式解析。我们点进去看这个抽象类DsProcessor,抽象方法doDetermineDatasource是有三个实现的:请求头处理器,Session处理器,表达式处理器

请求头和Session处理器很简单,就是从请求头里去拿,Session里拿直接的字符串,而表达式处理器则麻烦一些,在这里不深入了,想看的去看DsSpelExpressionProcessor类,就截个图略微看一下把

image-20240710140541724

image-20240710141058483

image-20240710141109817

image-20240710141036124

前面我们看到拦截器是实现MethodInterceptor实现的,那拦截的是哪些方法呢,我们看源码里的自动配置类,我们去看每个框架的时候都可以从关键功能或者自动配置文件去作为入口,在里面我们可以看到有个DynamicDataSourceAnnotationAdvisor,动态数据源注解通知,我们看源码里怎么写的

/*** 动态数据源核心自动配置类** @author TaoYu Kanyuxia* @see DynamicDataSourceProvider* @see DynamicDataSourceStrategy* @see DynamicRoutingDataSource* @since 1.0.0*/
@Slf4j
@Configuration
@AllArgsConstructor
@EnableConfigurationProperties(DynamicDataSourceProperties.class)
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
@Import(value = {DruidDynamicDataSourceConfiguration.class, DynamicDataSourceCreatorAutoConfiguration.class})
@ConditionalOnProperty(prefix = DynamicDataSourceProperties.PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true)
public class DynamicDataSourceAutoConfiguration {private final DynamicDataSourceProperties properties;@Bean@ConditionalOnMissingBeanpublic DynamicDataSourceProvider dynamicDataSourceProvider() {Map<String, DataSourceProperty> datasourceMap = properties.getDatasource();return new YmlDynamicDataSourceProvider(datasourceMap);}@Bean@ConditionalOnMissingBeanpublic DataSource dataSource(DynamicDataSourceProvider dynamicDataSourceProvider) {DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();dataSource.setPrimary(properties.getPrimary());dataSource.setStrict(properties.getStrict());dataSource.setStrategy(properties.getStrategy());dataSource.setProvider(dynamicDataSourceProvider);dataSource.setP6spy(properties.getP6spy());dataSource.setSeata(properties.getSeata());return dataSource;}@Bean@ConditionalOnMissingBeanpublic DynamicDataSourceAnnotationAdvisor dynamicDatasourceAnnotationAdvisor(DsProcessor dsProcessor) {DynamicDataSourceAnnotationInterceptor interceptor = new DynamicDataSourceAnnotationInterceptor();interceptor.setDsProcessor(dsProcessor);DynamicDataSourceAnnotationAdvisor advisor = new DynamicDataSourceAnnotationAdvisor(interceptor);advisor.setOrder(properties.getOrder());return advisor;}@Bean@ConditionalOnMissingBeanpublic DsProcessor dsProcessor() {DsHeaderProcessor headerProcessor = new DsHeaderProcessor();DsSessionProcessor sessionProcessor = new DsSessionProcessor();DsSpelExpressionProcessor spelExpressionProcessor = new DsSpelExpressionProcessor();headerProcessor.setNextProcessor(sessionProcessor);sessionProcessor.setNextProcessor(spelExpressionProcessor);return headerProcessor;}@Bean@ConditionalOnBean(DynamicDataSourceConfigure.class)public DynamicDataSourceAdvisor dynamicAdvisor(DynamicDataSourceConfigure dynamicDataSourceConfigure, DsProcessor dsProcessor) {DynamicDataSourceAdvisor advisor = new DynamicDataSourceAdvisor(dynamicDataSourceConfigure.getMatchers());advisor.setDsProcessor(dsProcessor);advisor.setOrder(Ordered.HIGHEST_PRECEDENCE);return advisor;}
}

我们可以看到源码里用spring aop的AnnotationMatchingPointcut 注解匹配切入点来对 方法/类上加了@DS注解的方法做了拦截。

image-20240710142338335

image-20240710142408701

在自动配置类最下面还有个AOP配置是DynamicDataSourceAdvisor,我们看源码理解,构建切入点Pointcut的时候用的DynamicJdkRegexpMethodPointcut,这个是继承的spring aop的JdkRegexpMethodPointcut,并多了matchesCache和ds两个字段。其实就是利用正则表达式来匹配方法,然后决定数据源。配置在配置文件中。而我们平时一般用注解的方式比较多。

/*** Copyright © 2018 organization baomidou* <pre>* Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**     http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.* <pre/>*/
package com.baomidou.dynamic.datasource.aop;import com.baomidou.dynamic.datasource.matcher.ExpressionMatcher;
import com.baomidou.dynamic.datasource.matcher.Matcher;
import com.baomidou.dynamic.datasource.matcher.RegexMatcher;
import com.baomidou.dynamic.datasource.processor.DsProcessor;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import lombok.Setter;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @author TaoYu* @since 1.2.0*/
public class DynamicDataSourceAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {/*** The identification of SPEL*/private static final String DYNAMIC_PREFIX = "#";@Setterprivate DsProcessor dsProcessor;private Advice advice;private Pointcut pointcut;private Map<String, String> matchesCache = new HashMap<>();public DynamicDataSourceAdvisor(List<Matcher> matchers) {this.pointcut = buildPointcut(matchers);this.advice = buildAdvice();}private Advice buildAdvice() {return new MethodInterceptor() {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {try {Method method = invocation.getMethod();String methodPath = invocation.getThis().getClass().getName() + "." + method.getName();String key = matchesCache.get(methodPath);if (key != null && !key.isEmpty() && key.startsWith(DYNAMIC_PREFIX)) {key = dsProcessor.determineDatasource(invocation, key);}DynamicDataSourceContextHolder.push(key);return invocation.proceed();} finally {DynamicDataSourceContextHolder.poll();}}};}@Overridepublic Pointcut getPointcut() {return this.pointcut;}@Overridepublic Advice getAdvice() {return this.advice;}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {if (this.advice instanceof BeanFactoryAware) {((BeanFactoryAware) this.advice).setBeanFactory(beanFactory);}}private Pointcut buildPointcut(List<Matcher> matchers) {ComposablePointcut composablePointcut = null;for (Matcher matcher : matchers) {if (matcher instanceof RegexMatcher) {RegexMatcher regexMatcher = (RegexMatcher) matcher;Pointcut pointcut = new DynamicJdkRegexpMethodPointcut(regexMatcher.getPattern(), regexMatcher.getDs(), matchesCache);if (composablePointcut == null) {composablePointcut = new ComposablePointcut(pointcut);} else {composablePointcut.union(pointcut);}} else {ExpressionMatcher expressionMatcher = (ExpressionMatcher) matcher;Pointcut pointcut = new DynamicAspectJExpressionPointcut(expressionMatcher.getExpression(), expressionMatcher.getDs(),matchesCache);if (composablePointcut == null) {composablePointcut = new ComposablePointcut(pointcut);} else {composablePointcut.union(pointcut);}}}return composablePointcut;}
}

image-20240710142802210

image-20240710142813234

数据源的确定,数据源的选择都已经知道了,我们看下数据源的加载,在自动配置类里有个DynamicDataSourceProvider我们点进去看其实就是个保存数据源名称和数据源包装对象的map罢了。而这个数据源的配置从哪里来的呢,是从DynamicDataSourceProperties来的,看下面截图,其实就是你在配置文件里配的spring.datasource.dynamic这个前缀下的配置。

image-20240710143343989

image-20240710143020793

image-20240710143325140

我们去Nacos配置里配的就是这样的配置,至此MybatisPlus多数据源的原理你就完整掌握啦

image-20240710143644313

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

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

相关文章

防御笔记第四天(持续更新)

1.状态检测技术 检测数据包是否符合协议的逻辑顺序&#xff1b;检查是否是逻辑上的首包&#xff0c;只有首包才会创建会话表。 状态检测机制可以选择关闭或则开启 [USG6000V1]firewall session link-state tcp ? check Indicate link state check [USG6000V1]firewall ses…

CLion学习笔记-cmake编译和多main函数编译

这里就不讲怎么配置clion了 项目名字 pcl_kdtree_search 1.新建一个工程名字自己取&#xff0c;我这里用自己学习pcl的&#xff0c;加一个main函数&#xff0c;这个时候Cmake里边就是这样的。 #声明要求的cmake最低版本 cmake_minimum_required(VERSION 3.19) #声明一个工程…

MongoDB 全文检索

MongoDB 全文检索 MongoDB 是一个流行的 NoSQL 数据库&#xff0c;以其灵活的数据模型和强大的查询语言而闻名。在 MongoDB 中&#xff0c;全文检索是一种功能&#xff0c;允许用户对存储在数据库中的文本数据进行复杂的搜索。全文检索对于构建搜索引擎、内容推荐系统和文本分…

【SQL】MySQL 中主要的锁类型

在MySQL中&#xff0c;主要有以下几种锁类型&#xff0c;每种锁都有不同的特点和使用场景&#xff1a; 1. 共享锁 (Shared Lock, S Lock) 共享锁是一种读取锁&#xff0c;也称为S锁。多个事务可以同时持有共享锁&#xff0c;并且不会阻塞其他事务获取共享锁&#xff0c;但会阻…

消息称台积电下周开始试产 2nm 芯片,有望率先用于苹果 iPhone 17

消息称台积电下周开始试产 2nm 芯片&#xff0c;有望率先用于苹果 iPhone 17 &#x1f4a1;&#x1f4f1; 大家好&#xff0c;我是猫头虎&#xff0c;科技自媒体博主 &#x1f431;&#x1f42f;&#xff0c;带你洞察科技世界的每一个细节&#xff01;&#x1f525; 关于猫头…

sklearn(Python机器学习库)介绍

0 引言 Sklearn (全称 Scikit-Learn)是基于Python 编程语言的免费软件机器学习库。 Scikit-learn主要是用Python编写的,它建立在 NumPy, SciPy, Pandas 和 Matplotlib 之上,里面API 的设计非常好,所有对象的接口简单,很适合新手上路。 Scikit-learn与许多其他Python库很好地…

Linux学习笔记(三)文件权限

一、权限的分类 Linux权限分为r(读取)、w(写入)、x(执行)。我们在终端执行ls -l命令查看文件详细信息显示如下: [root@srv sun]# ls -l 总用量 0 drwxr-xr-x. 2 sun root 6 7月 5 14:05 公共 drwxr-xr-x. 2 sun root 6 7月 5 14:05 模板 drwxr-xr-x. 2 sun root…

netty udp创建服务端+客户端

一.udp创建服务端 /*** udp 服务器 */ Slf4j Component public class UdpServer {/*** 创建服务端*/Asyncpublic void bind(int port) {EventLoopGroup group new NioEventLoopGroup();try {Bootstrap b new Bootstrap();b.group(group).channel(NioDatagramChannel.class).…

vue 导出excel乱码问题

今天做一个导出excel的功能&#xff0c;导出文件显示乱码&#xff0c;分析接口无问题&#xff0c;后修改如下&#xff1a; 1.接口的response类型&#xff1a;类型设置为blob // 导出信息 export const exportInfo (data: any, config { timeout: 6000, responseType: "…

MAT(Eclipse Memory Analyzer) Windows安装

MAT&#xff08;Eclipse Memory Analyzer&#xff09; Windows安装 MAT&#xff08;Eclipse Memory Analyzer&#xff09;是一个Java的内存分析工具 MAT下载地址 安装完成之后的目录机构 如若出现java版本过低的解决办法 在配置文件MemoryAnalyzer.ini中添加指定Java的版本…

PD协议诱骗芯片,XSP08Q,XSP16应用笔记

XSP08Q是3C数码或小家电产品的Type-C接口控制芯片&#xff0c;它负责和PD充电器通讯&#xff0c;获取充电器的快充电压档位&#xff0c;如5V4A&#xff0c;9V3A&#xff0c;12V2A&#xff0c;15V3A&#xff0c;20V5A等等。 XSP08Q支持PD协议&#xff0c;BC1.2协议&#xff0c;Q…

从产业链视角审视工作

从产业链视角审视工作&#xff1a;定位、价值与成长 作为一名技术博客博主&#xff0c;我经常收到各种关于职业发展、技术成长和学习路径的问题。最近&#xff0c;我看了一份学习报告&#xff0c;其中提到了一种非常有趣且实用的视角——从产业链的角度去审视自己的工作。这种视…

Hash算法和MD5容易受到冲突攻击的原因

Hash算法和MD5容易受到冲突攻击的原因主要在于它们的设计缺陷和安全性问题。‌ 设计缺陷&#xff1a;‌如果哈希算法设计不当&#xff0c;‌可能会产生哈希碰撞&#xff0c;‌甚至导致碰撞攻击。‌哈希函数的目标是将输入数据转换成固定长度的输出&#xff0c;‌但如果在设计上…

Web 自动化测试主流框架都有哪些?

Web移动端自动化测试成为了现代软件开发流程中的重要环节&#xff0c;因此&#xff0c;很多主流框架被开发出来来帮助开发人员提高测试效率。本篇文章将从零到一详细介绍Web移动端自动化测试的主流框架。 一、Web移动端自动化测试框架简介 Web移动端自动化测试框架是一种开发工…

百元平价蓝牙耳机哪款好?平价高性价比蓝牙耳机推荐

随着蓝牙耳机的普及&#xff0c;市面上各种品牌的蓝牙耳机也层出不穷。对于那些预算在百元平价的朋友来说&#xff0c;百元平价蓝牙耳机哪款好&#xff1f;这个问题就显得格外重要了。毕竟&#xff0c;蓝牙耳机作为日常生活中不可或缺的小伙伴&#xff0c;不仅需要音质出众、续…

AD3518 SOP-8封装 单节锂电池保护芯片 可替代XB8608/XB8608A

AD3518 是一款内置 MOSFET 的单节锂电池保护芯片。该芯片具有非常低的功耗和非常低阻抗的内置 MOSFET。该芯片有充电过压&#xff0c;充电过流&#xff0c;放电过压&#xff0c;放电过流&#xff0c;过热&#xff0c;短路&#xff0c;电芯反接等各项保护等功能&#xff0c;确保…

7.深度学习概述

深度学习概述 1. 线性回归1.1 线性回归一般表达式1.2 线性回归内积表达方式&#xff1a;1.3 多个样本时&#xff0c;线性回归的进一步表达&#xff1a;1.4 线性回归方程的解析1.5 线性回归就是求loss函数的最小值 2. 如何求函数最小值2.1 一个例子2.2 求导法——求最小值2.3 求…

OCR的基本概念和开源项目介绍

深度学习和OCR OCR基本概念开源项目多模态大模型——OCR free 在当今多模态大模型横行的时代&#xff0c;已经没有人再去研究OCR了 OCR基本概念 OCR是目标检测类项目&#xff0c;但又不完全是目标检测。它是 行级语义OCR问题最有效解决方案&#xff1a;OCR问题&#xff1a; 第…

使用“nvm use 版本号“命令无效

使用"nvm use 版本号"命令无效 为什么无效?解决 为什么无效? 解决 将这个nodejs文件夹删除,然后在运行nvm use 版本号,则 node生效.

C#面:解释startup class的configure方法有什么作用?

这个⽅法来定义整个应⽤如何响应HTTP请求。它有⼏个⽐较重要的参数&#xff0c;application builder&#xff0c;Hosting environment, logo factory&#xff0c; 在这⾥我们可以配置⼀些中间件⽤来处理路径&#xff0c;验证和session等等。 startup class的configure方法是 A…