SpringCloud之nacos共享配置文件实现多数据源灵活切换
目录
前言
1.引入Springboot相关的aop切面依赖
2.创建自定义注解@DataSourceKey
3.创建对ThreadLocal类
4.创建aop切面
5.创建动态数据源类
6.创建多数据库连接配置类
7.关键代码讲解
8.nacos主要配置
前言
通过Spring AOP(面向切面编程)的功能来动态地切换数据源。使用@Aspect和@Component注解,通过切面扫描自定义注解,获取数据源的key,
可以在不修改原有业务代码的情况下,在service里面的类方法中加入@DataSourceKey注解,即可访问指定的数据源。
1.引入Springboot相关的aop切面依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>
2.创建自定义注解@DataSourceKey
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSourceKey {//默认使用auth数据库String value() default "dataSourceSystem";
}
3.创建对ThreadLocal类
通过线程隔离的方式,实现数据源的切换。
package com.example.auth.datasource;/*** 数据库上下文切换对象,针对每个线程做不同操作*/
public class DataSourceContextHolder {private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();public static void setDataSourceKey(String dataSourceKey) {contextHolder.set(dataSourceKey);}public static String getDataSourceKey() {return contextHolder.get();}public static void clearDataSourceKey() {contextHolder.remove();}
}
4.创建aop切面
通过包扫描动态切换数据源,主要通过扫描注解的方式获取数据源的key值,即数据源名称。
package com.example.auth.datasource;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.DeclareAnnotation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/*** 多数据源切面*/
@Aspect
@Component
public class DatasourceAspect {private Logger logger = LoggerFactory.getLogger(DatasourceAspect.class);@Before("@annotation(dataSourceKey) && execution(* com.example.auth.datasource.*.*(..))")public void beforeSwitchDataSource(JoinPoint joinPoint, DataSourceKey dataSourceKey) {String key = dataSourceKey.value();logger.info("key:{}",key);DataSourceContextHolder.setDataSourceKey(key);}@Before("@annotation(dataSourceKey) && execution(* com.example.auth.service.*.*(..))")public void beforeServiceSwitchDataSource(JoinPoint joinPoint, DataSourceKey dataSourceKey) {String key = dataSourceKey.value();logger.info("key:{}",key);DataSourceContextHolder.setDataSourceKey(key);}@After("@annotation(dataSourceKey) && execution(* com.example.auth.service.*.*(..))")public void afterServiceSwitchDataSource(JoinPoint joinPoint, DataSourceKey dataSourceKey) {String key = dataSourceKey.value();logger.info("key:{}",key);DataSourceContextHolder.clearDataSourceKey();}@After("@annotation(dataSourceKey) && execution(* com.example.auth.datasource.*.*(..)) ")public void afterSwitchDataSource(JoinPoint joinPoint, DataSourceKey dataSourceKey) {logger.info("key:{}",dataSourceKey.value());DataSourceContextHolder.clearDataSourceKey();}}
5.创建动态数据源类
通过该类,可动态改变数据源名称。
package com.example.auth.datasource;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;public class DynamicDataSource extends AbstractRoutingDataSource {private Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);@Overrideprotected Object determineCurrentLookupKey() {String key = DataSourceContextHolder.getDataSourceKey();logger.info("数据源:{}",key);return DataSourceContextHolder.getDataSourceKey();}
}
6.创建多数据库连接配置类
package com.example.auth.datasource;import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.session.SqlSessionFactory;
import org.jasypt.encryption.StringEncryptor;
import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import javax.activation.DataContentHandler;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;/*** 多数据源配置*/
@Configuration
@MapperScan(basePackages = "com.example.auth.mapper")
public class MultiDataSourceConfig {private Logger logger = LoggerFactory.getLogger(MultiDataSourceConfig.class);@Autowiredprivate DataSource dataSourceAuth;@Autowiredprivate DataSource dataSourceConsumer;@Autowiredprivate DataSource dataSourceMq;@Autowiredprivate DataSource dataSourceGateway;@Autowiredprivate DataSource dataSourceSystem;@Autowired@Qualifier("dynamicDataSource")private DataSource dynamicDataSource;@Autowiredprivate StringEncryptor stringEncryptor;@Bean(name = "dataSourceAuth")@ConfigurationProperties(prefix = "spring.datasource.auth")public DataSource dataSourceAuth() {return DataSourceBuilder.create().build();}@Bean(name = "dataSourceConsumer")@ConfigurationProperties(prefix = "spring.datasource.consumer")public DataSource dataSourceConsumer() {return DataSourceBuilder.create().build();}@Bean(name = "dataSourceMq")@ConfigurationProperties(prefix = "spring.datasource.mq")public DataSource dataSourceMq() {return DataSourceBuilder.create().build();}@Bean(name = "dataSourceGateway")@ConfigurationProperties(prefix = "spring.datasource.gateway")public DataSource dataSourceGateway() {return DataSourceBuilder.create().build();}@Bean(name = "dataSourceSystem")@ConfigurationProperties(prefix = "spring.datasource.system")public DataSource dataSourceSystem() {return DataSourceBuilder.create().build();}@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//分页插件interceptor.addInnerInterceptor(new PaginationInnerInterceptor());//注册乐观锁插件interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor;}@Beanpublic StringEncryptor stringEncryptor() {PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();SimpleStringPBEConfig config = new SimpleStringPBEConfig();config.setPassword("encryptionkey"); // 加密密钥config.setAlgorithm("PBEWithHmacSHA512AndAES_256");config.setKeyObtentionIterations("1000");config.setPoolSize("1");config.setProviderName("SunJCE");config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");config.setStringOutputType("base64");encryptor.setConfig(config);return encryptor;}@PostConstructpublic void init(){/* String enStr = stringEncryptor.encrypt("Root@123");String deSTr = stringEncryptor.decrypt("N8VBWG5nOHvy5efX3/mlPAmdBykE7iDZFl362LyeaPRXMbLT0PzEIlB/KDXrNYz6");System.out.println("enStr==="+enStr);System.out.println("deSTr==="+deSTr);*/}/*** 不加* @param interceptor* @return* @throws Exception*/@Beanpublic SqlSessionFactory sqlSessionFactory (MybatisPlusInterceptor interceptor) throws Exception {MybatisSqlSessionFactoryBean ssfb = new MybatisSqlSessionFactoryBean();ssfb.setDataSource(dynamicDataSource); // 使用 DynamicDataSourcessfb.setPlugins(interceptor);ssfb.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/*Mapper.xml"));return ssfb.getObject();}@Beanpublic DataSource dynamicDataSource() {DynamicDataSource dynamicDataSource = new DynamicDataSource();// 假设你有多个数据源,需要在这里将它们添加到 targetDataSources 中Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put("dataSourceSystem", dataSourceSystem);targetDataSources.put("dataSourceAuth", dataSourceAuth);targetDataSources.put("dataSourceConsumer", dataSourceConsumer);targetDataSources.put("dataSourceMq", dataSourceMq);targetDataSources.put("dataSourceGateway",dataSourceGateway);dynamicDataSource.setTargetDataSources(targetDataSources);dynamicDataSource.setDefaultTargetDataSource(dataSourceSystem);// 设置默认数据源return dynamicDataSource;}}
7.关键代码讲解
注入dynamicDataSource实体,通过该实体bean动态获取数据源,从而达到随意切换数据源的目的。
单个dataSource的注入,如 dataSourceAuth,主要是给动态数据源的切换提前准备多数据源。
8.nacos主要配置
spring:datasource:system: driver-class-name: com.mysql.cj.jdbc.Driverjdbc-url: jdbc:mysql://localhost:3306/system?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&nullCatalogMeansCurrent=trueusername: rootpassword: ENC(N8VBWG5nOHvy5efX3/mlPAmdBykE7iDZFl362LyeaPRXMbLT0PzEIlB/KDXrNYz6)type: com.alibaba.druid.pool.DruidDataSourcedruid:initial-size: 5min-idle: 1max-active: 10max-wait: 60000validation-query: SELECT 1 FROM DUALtest-on-borrow: falsetest-on-return: falsetest-while-idle: truetime-between-eviction-runs-millis: 60000auth: driver-class-name: com.mysql.cj.jdbc.Driverjdbc-url: jdbc:mysql://localhost:3306/auth?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&nullCatalogMeansCurrent=trueusername: rootpassword: ENC(N8VBWG5nOHvy5efX3/mlPAmdBykE7iDZFl362LyeaPRXMbLT0PzEIlB/KDXrNYz6)type: com.alibaba.druid.pool.DruidDataSourcedruid:initial-size: 5min-idle: 1max-active: 10max-wait: 60000validation-query: SELECT 1 FROM DUALtest-on-borrow: falsetest-on-return: falsetest-while-idle: truetime-between-eviction-runs-millis: 60000consumer: driver-class-name: com.mysql.cj.jdbc.Driverjdbc-url: jdbc:mysql://localhost:3306/consumer?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&nullCatalogMeansCurrent=trueusername: rootpassword: ENC(N8VBWG5nOHvy5efX3/mlPAmdBykE7iDZFl362LyeaPRXMbLT0PzEIlB/KDXrNYz6)type: com.alibaba.druid.pool.DruidDataSourcedruid:initial-size: 5min-idle: 1max-active: 10max-wait: 60000validation-query: SELECT 1 FROM DUALtest-on-borrow: falsetest-on-return: falsetest-while-idle: truetime-between-eviction-runs-millis: 60000mq: driver-class-name: com.mysql.cj.jdbc.Driverjdbc-url: jdbc:mysql://localhost:3306/mq?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&nullCatalogMeansCurrent=trueusername: rootpassword: ENC(N8VBWG5nOHvy5efX3/mlPAmdBykE7iDZFl362LyeaPRXMbLT0PzEIlB/KDXrNYz6)type: com.alibaba.druid.pool.DruidDataSourcedruid:initial-size: 5min-idle: 1max-active: 10max-wait: 60000validation-query: SELECT 1 FROM DUALtest-on-borrow: falsetest-on-return: falsetest-while-idle: truetime-between-eviction-runs-millis: 60000gateway: driver-class-name: com.mysql.cj.jdbc.Driverjdbc-url: jdbc:mysql://localhost:3306/gateway?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&nullCatalogMeansCurrent=trueusername: rootpassword: ENC(N8VBWG5nOHvy5efX3/mlPAmdBykE7iDZFl362LyeaPRXMbLT0PzEIlB/KDXrNYz6)type: com.alibaba.druid.pool.DruidDataSourcedruid:initial-size: 5min-idle: 1max-active: 10max-wait: 60000validation-query: SELECT 1 FROM DUALtest-on-borrow: falsetest-on-return: falsetest-while-idle: truetime-between-eviction-runs-millis: 60000
相关文章:
SpringCloud之nacos共享配置文件实现多数据源灵活切换
目录 前言 1.引入Springboot相关的aop切面依赖 2.创建自定义注解DataSourceKey 3.创建对ThreadLocal类 4.创建aop切面 5.创建动态数据源类 6.创建多数据库连接配置类 7.关键代码讲解 8.nacos主要配置 前言 通过Spring AOP(面向切面编程)的功能来动…...

原生小程序生成二维码方法之一
效果图: 第一步:下载对应的包并构建(工具---》构建npm) npm install weapp-qrcode --save 第二步:在wxml页面声明canvas <canvas style"width: 200px; height: 200px;margin:0 auto;" canvas-id"myQ…...

Kubernetes k8s Pod容器 探针 健康探测
目录 Pod容器健康探测 为什么要对容器做探测? 启动探测startupprobe 存活性探测livenessProbe 就绪性探测readinessProbe ReadinessProbe LivenessProbe startupProbe配合使用示例一般程序中需要设置三种探针结合使用,并且也要结合实际情况ÿ…...

Conformal low power-2.电源感知等效性检查
电源感知等效性检查 ■ 第24页:电源感知等效性检查概述 ■ 第24页:启动低功耗(等效性检查)软件 ■ 第25页:电源感知等效性检查流程 ■ 第28页:电源感知等效性检查示例Do文件 电源感知等效性检查概述…...

【密码学】从有限状态自动机到密钥流生成器
本文是对流密码内容的拓展,在流密码中种子密钥通过一个伪随机数生成器产生一个与明文等长的伪随机密钥流。而本文的内容就是在回答这样两个问题: 伪随机密钥流是如何生成的?流密码、流密钥生成器和有限状态自动机之间是什么关系?…...

3.相机标定原理及代码实现(opencv)
1.相机标定原理 相机参数的确定过程就叫做相机标定。 1.1 四大坐标系及关系 (1)像素坐标系(单位:像素(pixel)) 像素坐标系是指相机拍到的图片的坐标系,以图片的左上角为坐标原点&a…...

Centos7 安装Docker步骤及报错信息(不敢说最全,但是很全)
一、操作系统要求: 要安装Docker Engine,您需要CentOS 7及以上的维护版本。存档版本不受支持或测试。必须启用centos临时存储库。默认情况下,此存储库已启用,但如果已禁用,则需要重新启用它。建议使用overlay2存储驱动…...
【C语言】符号优先级详解
C语言符号优先级详细解析 在C语言中,不同的运算符具有不同的优先级和结合性,这决定了在表达式中运算符的计算顺序。理解这些优先级和结合性是正确编写和理解C语言程序的基础。本文将详细解析C语言中的符号优先级,包括各类运算符的优先级、结…...

天翼云高级运维工程师202407回忆题库 最新出炉
备考天翼云高级运维工程师 必须备考天翼云 之前觉得外企牛批 然后民企,拔地而起,民企也不错,工资高,有钱途 现在看来看去,还是国企好,体制内的,有保障,树大根深 有必要备考下天…...
在Python中什么是上下文管理器以及如何使用with语句来管理资源
什么是上下文管理器? 在Python中,上下文管理器(Context Manager)是一种支持with语句的协议,允许对象管理资源,如文件、线程锁的获取和释放、数据库连接等。上下文管理器负责资源的分配和释放,确…...

(四)、python程序--贪吃蛇游戏
一、绪论 贪吃蛇游戏。 已实现功能: 1、上下左右移动; 2、吃食物,随机生成食物; 3、碰撞检测,判断是否游戏结束。 二、代码分享 1、main.py import pygame import sys import food as c_food import snake as c…...
什么是DNS欺骗
DNS欺骗(DNS Spoofing),也称为DNS缓存中毒(DNS Cache Poisoning),是一种网络攻击形式,攻击者通过操纵DNS记录,将用户重定向到一个伪造的、恶意的网站。这些恶意网站可能看起来与用户…...
C++实现对结构体信息排序
思路解读: 定义结构体 Student: 结构体 Student 用来表示学生信息,包含两个成员变量:name(学生姓名)和 score(学生分数)。Student 结构体定义了一个构造函数,用于初始化 name 和 sco…...

[CTF]-PWN:House of Cat堆题型综合解析
原理: 调用顺序: exit->_IO_wfile_jumps->_IO_wfile_seekoff->_IO_switch_to_wget_mode _IO_wfile_seekoff源码: off64_t _IO_wfile_seekoff (FILE *fp, off64_t offset, int dir, int mode) {off64_t result;off64_t delta, new…...

18.按键消抖模块设计(使用状态机,独热码编码)
(1)设计意义:按键消抖主要针对的时机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子就断开。因而在闭合以及断开的瞬…...

【Hec-HMS】第一期:模型简介及软件安装
HEC-HMS模型简介及软件安装 HEC-HMS模型简介建模思路 HEC-HMS软件安装步骤1:安装InstallShield Wizard步骤2:安装HEC-HMS 参考 HEC-HMS模型简介 HEC-HMS(The Hydrologic Engineering Center’s-Hydrologic Modelimng System),美国陆军工程兵…...
逻辑回归不是回归吗?那为什么叫回归?
RNN 逻辑回归不是回归吗?那为什么叫回归?逻辑回归的基本原理逻辑函数(Sigmoid函数)二元分类 为什么叫做“回归”?逻辑回归的应用场景总结 逻辑回归不是回归吗?那为什么叫回归? 逻辑回归&#x…...
Activity对象的部分常见成员变量
在Android开发中,Activity 类是一个非常重要的类,它代表了应用程序中的一个屏幕。每个Activity都有一系列的成员变量和方法,这些成员变量通常用于控制和管理活动生命周期、UI界面元素、应用资源等。虽然具体的成员变量会根据Android的不同版本…...
量化交易策略:赌徒在股市会运用凯利公式(附python代码)
一、凯利公式的历史 凯利公式(Kelly Criterion)是由美国贝尔实验室物理学家约翰拉里凯利(John Larry Kelly)于1956年提出的,用于计算最优投资比例的一种数学公式。凯利公式的核心思想是:在期望收益和风险之间找到一个平衡点,使得投资者在承担一定风险的情况下,能够获得…...
信息系统项目管理师【一】英文选择题词汇大全(1)
一、计算机相关词汇 数据挖掘 Data Mining分布式计算 Distributed Computing云计算 Cloud Computing物联网 IOT Internet of Things大数据 Big Data人工智能 artificial intelligence互联网 Internet plus区块链 Blockchain5G 5th-Generation感知层 sensing layer机器学习 mac…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...

.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...

聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...

苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...