Spring Boot集成Druid实现多数据源的两种方式

news2024/10/5 19:21:00

目录

项目场景:

一、集成com.baomidou的方式

1、maven依赖:

2、 配置文件:

3、 使用注解切换数据源: 

二、基于AOP手动实现多数据源原生的方式

1、maven依赖:

2、 配置文件:

3、 多数据源名称类

4、自定义注解 

5、配置类

6、动态数据源配置

 7、AOP切面拦截注解

8、Dao层,跟正常一样的

 9、service层,加上多数据源注解

 10、Controller测试

三、结果展示


项目场景:

Spring Boot集成Druid实现多数据源的两种方式:

1、集成com.baomidou,引入dynamic-datasource依赖;

2、原生的方式,基于AOP手动实现多数据源;


一、集成com.baomidou的方式

1、maven依赖:

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.5.1</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
</dependency>

2、 配置文件:

1.spring.datasource.dynamic前缀,是baomidou源码里固定的;

2.first、second是自定义的名称,可以更改;

# 多数据源配置,默认master
spring.datasource.dynamic.primary = first
# 数据源1
spring.datasource.dynamic.datasource.first.driverClassName = com.mysql.cj.jdbc.Driver
spring.datasource.dynamic.datasource.first.type = com.alibaba.druid.pool.DruidDataSource
spring.datasource.dynamic.datasource.first.url = jdbc:mysql://127.0.0.1:3306/mp_dm?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.dynamic.datasource.first.username = test
spring.datasource.dynamic.datasource.first.password = test1
# 数据源2
spring.datasource.dynamic.datasource.second.driverClassName = com.mysql.cj.jdbc.Driver
spring.datasource.dynamic.datasource.second.type = com.alibaba.druid.pool.DruidDataSource
spring.datasource.dynamic.datasource.second.url = jdbc:mysql://127.0.0.1:18306/mp_dm?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.dynamic.datasource.second.username = test
spring.datasource.dynamic.datasource.second.password = test1

# druid多数据源全局配置
spring.datasource.dynamic.druid.filter.stat.log-slow-sql = true
spring.datasource.dynamic.druid.filter.stat.slow-sql-millis = 1000
spring.datasource.dynamic.druid.min-evictable-idle-time-millis = 300000
spring.datasource.dynamic.druid.test-on-borrow = false
spring.datasource.dynamic.druid.filter.stat.merge-sql = false
spring.datasource.dynamic.druid.test-on-return = false
spring.datasource.dynamic.druid.initial-size = 10
spring.datasource.dynamic.druid.min-idle = 10
spring.datasource.dynamic.druid.max-wait = 60000
spring.datasource.dynamic.druid.pool-prepared-statements = true
spring.datasource.dynamic.druid.test-while-idle = true
spring.datasource.dynamic.druid.validation-query = select 1
spring.datasource.dynamic.druid.filter.wall.config.multi-statement-allow = true
spring.datasource.dynamic.druid.time-between-eviction-runs-millis = 60000
spring.datasource.dynamic.druid.max-pool-prepared-statement-per-connection-size = 20
spring.datasource.dynamic.druid.max-active = 100
# druid监控全局配置
spring.datasource.druid.stat-view-servlet.enabled = true
spring.datasource.druid.stat-view-servlet.url-pattern = /druid/*

验证配置文件是否生效:Spring Boot集成Druid查看配置是否生效_涛哥是个大帅比的博客-CSDN博客 

3、 使用注解切换数据源: 

@DS注解说明:

1.注解在方法上、类上、接口、枚举,同时存在就近原则,方法上注解优先于类上注解;

2.不使用@DS注解,默认主数据源;

类上:

@DS("db2")
public class test(){}

 方法上:

@DS("db2")
public void test(){}

mapper方法上

@Select("SELECT * FROM TEST WHERE A = #{A}")
@DS("db2")
Map<String, Object> test(@Param("A") String A);

多数据源可能会碰到事务问题:
 Spring Boot多数据源事务@DSTransactional的使用_涛哥是个大帅比的博客-CSDN博客


二、基于AOP手动实现多数据源原生的方式

1、maven依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
</dependency>

2、 配置文件:

1.Spring Boot 2.X 版本不再支持配置继承,多数据源的话每个数据源的所有配置都需要单独配置,否则配置不会生效;

2.first、second是自定义的名称,对应DynamicDataSourceConfig配置类,可以更改;

# 多数据源配置
spring.datasource.type = com.alibaba.druid.pool.DruidDataSource
# 数据源1
spring.datasource.druid.first.name = first
spring.datasource.druid.first.driverClassName = com.mysql.cj.jdbc.Driver
spring.datasource.druid.first.url = jdbc:mysql://127.0.0.1:3306/mp_dm?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.druid.first.username = test
spring.datasource.druid.first.password = test1
# 数据源2
spring.datasource.druid.second.name = second
spring.datasource.druid.second.driverClassName = com.mysql.cj.jdbc.Driver
spring.datasource.druid.second.url = jdbc:mysql://127.0.0.1:18306/mp_dm?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.druid.second.username = test
spring.datasource.druid.second.password = test1

# druid数据源1
spring.datasource.druid.first.filter.stat.log-slow-sql = true
spring.datasource.druid.first.filter.stat.slow-sql-millis = 1000
spring.datasource.druid.first.min-evictable-idle-time-millis = 300000
spring.datasource.druid.test-on-borrow = false
spring.datasource.druid.first.filter.stat.merge-sql = false
spring.datasource.druid.test-on-return = false
spring.datasource.druid.first.initial-size = 10
spring.datasource.druid.first.min-idle = 10
spring.datasource.druid.first.max-wait = 60000
spring.datasource.druid.first.pool-prepared-statements = true
spring.datasource.druid.first.test-while-idle = true
spring.datasource.druid.first.validation-query = select 1
spring.datasource.druid.first.filter.wall.config.multi-statement-allow = true
spring.datasource.druid.first.time-between-eviction-runs-millis = 60000
spring.datasource.druid.first.max-pool-prepared-statement-per-connection-size = 20
spring.datasource.druid.first.max-active = 100
# druid数据源2
spring.datasource.druid.second.filter.stat.log-slow-sql = true
spring.datasource.druid.second.filter.stat.slow-sql-millis = 1000
spring.datasource.druid.second.min-evictable-idle-time-millis = 300000
spring.datasource.druid.second.test-on-borrow = false
spring.datasource.druid.second.filter.stat.merge-sql = false
spring.datasource.druid.second.test-on-return = false
spring.datasource.druid.second.initial-size = 10
spring.datasource.druid.second.min-idle = 10
spring.datasource.druid.second.max-wait = 60000
spring.datasource.druid.second.pool-prepared-statements = true
spring.datasource.druid.second.test-while-idle = true
spring.datasource.druid.second.validation-query = select 1
spring.datasource.druid.second.filter.wall.config.multi-statement-allow = true
spring.datasource.druid.second.time-between-eviction-runs-millis = 60000
spring.datasource.druid.second.max-pool-prepared-statement-per-connection-size = 20
spring.datasource.druid.second.max-active = 100
# druid监控全局配置
spring.datasource.druid.stat-view-servlet.enabled = true
spring.datasource.druid.stat-view-servlet.url-pattern = /druid/*

验证配置文件是否生效:Spring Boot集成Druid查看配置是否生效_涛哥是个大帅比的博客-CSDN博客 

3、 多数据源名称类

package com.test.datasources;

/**
 * 增加多数据源,在此配置
 */
public interface DataSourceNames {
    String FIRST = "first";
    String SECOND = "second";

} 

4、自定义注解 

package com.test.datasources.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 多数据源注解
 */
//同时支持方法注解和类注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    String value() default "";
}

5、配置类

package com.test.datasources;

import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;

/**
 * 配置多数据源
 */
@Configuration
public class DynamicDataSourceConfig {

    @Bean
    @ConfigurationProperties("spring.datasource.druid.first")
    public DataSource firstDataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.druid.second")
    public DataSource secondDataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public DynamicDataSource dataSource(DataSource firstDataSource, DataSource secondDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceNames.FIRST, firstDataSource);
        targetDataSources.put(DataSourceNames.SECOND, secondDataSource);
        return new DynamicDataSource(firstDataSource, targetDataSources);
    }
    
    @Bean
    public JdbcTemplate jdbcTemplate() {
     return new JdbcTemplate(firstDataSource());
    }
}

6、动态数据源配置

package com.test.datasources;

import java.util.Map;

import javax.sql.DataSource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * 动态数据源,关键代码
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return getDataSource();
    }

    public static void setDataSource(String dataSource) {
        contextHolder.set(dataSource);
    }

    public static String getDataSource() {
        return contextHolder.get();
    }

    public static void clearDataSource() {
        contextHolder.remove();
    }

}

 7、AOP切面拦截注解

package com.test.datasources.aspect;

import java.lang.reflect.Method;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

import com.test.datasources.DataSourceNames;
import com.test.datasources.DynamicDataSource;
import com.test.datasources.annotation.DataSource;

/**
 * 多数据源,切面处理类
 */
@Aspect
@Component
public class DataSourceAspect implements Ordered {
    protected Logger logger = LoggerFactory.getLogger(getClass());

    /*
     * @annotation匹配指定注解的方法
     * @within匹配指定注解的类
     * 注意:这里只拦截所注解的类,如果调用的是父类的方法,那么不会拦截,除非父类方法在子类中被覆盖。
     */
    @Pointcut("@annotation(com.test.datasources.annotation.DataSource) || @within(com.test.datasources.annotation.DataSource)")
    public void dataSourcePointCut() {

    }

    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Class<?> dataClass = Class.forName(signature.getDeclaringTypeName());
        
        DataSource dsMethod = method.getAnnotation(DataSource.class);
        DataSource dsClass = dataClass.getAnnotation(DataSource.class);
        if(dsMethod != null){
        	//方法优先,如果方法上存在注解,则优先使用方法上的注解
        	DynamicDataSource.setDataSource(dsMethod.value());
            logger.debug("set datasource is " + dsMethod.value());
        }else if(dsClass != null){
            //其次类优先,如果类上存在注解,则使用类上的注解
        	DynamicDataSource.setDataSource(dsClass.value());
            logger.debug("set datasource is " + dsClass.value());
        }else{
        	//如果都不存在,则使用默认
        	DynamicDataSource.setDataSource(DataSourceNames.FIRST);
            logger.debug("set datasource is " + DataSourceNames.FIRST);
        }

        try {
            return point.proceed();
        } finally {
            DynamicDataSource.clearDataSource();
            logger.debug("clean datasource");
        }
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

8、Dao层,跟正常一样的

package com.test.mapper;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import com.test.entity.Test1Entity;

@Mapper
public interface Test1Dao {
	
	@Select("select * from test1")
	Test1Entity getById(@Param("id")Integer id);
}
package com.test.mapper;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import com.test.entity.Test2Entity;

@Mapper
public interface Test2Dao {
	@Select("select * from test2")
	Test2Entity getById(@Param("id")Integer id);
}

 9、service层,加上多数据源注解

package com.test.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.test.datasources.DataSourceNames;
import com.test.datasources.annotation.DataSource;
import com.test.entity.Test1Entity;
import com.test.mapper.Test1Dao;

@Service
@DataSource(DataSourceNames.FIRST)
public class Test1Service {

	@Autowired
	private Test1Dao test1Dao;
	
	public Test1Entity getById(int id) {
		return test1Dao.getById(id);
	}
}
package com.test.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.test.datasources.DataSourceNames;
import com.test.datasources.annotation.DataSource;
import com.test.entity.Test2Entity;
import com.test.mapper.Test2Dao;

@Service
@DataSource(DataSourceNames.SECOND)
public class Test2Service  {

	@Autowired
	private Test2Dao test2Dao;
	
	public Test2Entity getById(int id) {
		return test2Dao.getById(id);
	}
}

 10、Controller测试

package com.test.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.test.entity.Test1Entity;
import com.test.entity.Test2Entity;
import com.test.service.Test1Service;
import com.test.service.Test2Service;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

/**
 * 动态数据源测试
 */
@Api(tags = "动态数据源测试")
@RestController
@RequestMapping("/test")
public class DynamicController {

	@Autowired
	private Test1Service test1Service;
	@Autowired
	private Test2Service test2Service;
	
	@ApiOperation(value = "数据源1")
	@RequestMapping(value = "/test1", method = RequestMethod.GET)
	public String test1() {
		Test1Entity entity = test1Service.getById(1);
		return entity.getName();
	}
	
	@ApiOperation(value = "数据源2")
	@RequestMapping(value = "/test2", method = RequestMethod.GET)
	public String test2() {
		Test2Entity entity = test2Service.getById(2);
		return entity.getName();
	}
}

多数据源可能会碰到事务问题:https://blog.csdn.net/u011974797/article/details/130154340

三、结果展示

服务启动会打印日志:

com.alibaba.druid.pool.DruidDataSource   : {dataSource-1,first} inited
com.alibaba.druid.pool.DruidDataSource   : {dataSource-2,second} inited

druid监控页面:

 

源码地址:https://download.csdn.net/download/u011974797/87680942

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

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

相关文章

数字中国理念引领国企人力资源数字化转型与实践

3月初&#xff0c;《数字中国建设整体布局规划》出台&#xff0c;作为中国数字经济发展的重要指导性文件&#xff0c;规划提出了数字中国建设的总体目标和战略方向&#xff0c;文件详细阐述了数字中国内涵&#xff0c;包含数字经济、数字社会、数字政府以及数字生态等内涵。《规…

制造企业该如何选择MES生产管理系统?盘点四大生产管理系统软件

本文将介绍&#xff1a;1、如何选择MES(生产管理系统&#xff09;&#xff1b;2、盘点四款好用的生产管理系统 生产管理系统即MES(Manufacturing Execution System)&#xff0c;制造执行系统。是面向车间生产的管理系统。在产品从工单发出到成品完工的过程中&#xff0c;MES系…

1206. 设计跳表

1206. 设计跳表 不使用任何库函数&#xff0c;设计一个 跳表 。 跳表 是在 O(log(n)) 时间内完成增加、删除、搜索操作的数据结构。跳表相比于树堆与红黑树&#xff0c;其功能与性能相当&#xff0c;并且跳表的代码长度相较下更短&#xff0c;其设计思想与链表相似。 例如&a…

如何收集EMC VPLEX 日志和VPLEX日志的简单解读

对于VPLEX遇到的问题&#xff0c;和二线沟通最快最有效的办法就是收集完整的日志&#xff0c;而不是拍一个照片。本文就详细介绍如何收集日志&#xff1f;和那些日志文件对我们分析问题是有价值的。 命令行ssh登录Vplex 管理控制台&#xff0c;然后进入Vplexcli命令行&#xf…

Windows安装Nginx并配置负载均衡

Nginx简介 Nginx是一款自由的、开源的、高性能的HTTP服务器和反向代理服务器&#xff1b;同时也是一个IMAP、POP3、SMTP代理服务器&#xff1b;Nginx可以作为一个HTTP服务器进行网站的发布处理&#xff0c;另外Nginx可以作为反向代理进行负载均衡的实现。 Nginx使用基于事件驱动…

HOJ项目部署(前后端及其判题机)

文章目录HOJ项目部署1 项目准备1.1 拉取HOJ项目到本地1.2 项目包结构解读2 后端部署DataBackup2.1 环境准备1&#xff09;项目基本数据导入2&#xff09;Nacos环境配置2.2 修改配置1&#xff09;修改application-prod.yml2&#xff09;修改bootstrap.yml3&#xff09;修改上传文…

【模板特性补充】

目录&#xff1a;前言一、非类型模板参数使用方法使用场景二、模板特化&#xff08;一&#xff09;函数模板特化&#xff08;二&#xff09;类模板特化1.全特化2.偏特化使用场景三、模板分离编译1. 什么是分离编译2.模板的分离编译3.解决方法四、模板总结前言 打怪升级&#x…

你知道多少接口性能优化的方案?

一、背景 针对老项目&#xff0c;去年做了许多降本增效的事情&#xff0c; 二、接口优化方案总结 1.批处理 批量思想&#xff1a;批量操作数据库&#xff0c;这个很好理解&#xff0c;我们在循环插入场景的接口中&#xff0c;可以在批处理执行完成后一次性插入或更新数据库…

Springboot的自动注入

一、开篇 在平时的开发过程中用的最多的莫属springboot了&#xff0c;都知道springboot中有自动注入的功能&#xff0c;在面试过程中也会问到自动注入&#xff0c;你知道自动注入是怎么回事吗&#xff0c;springboot是如何做到自动注入的&#xff0c;自动注入背后的原理是什么&…

基于 RocketMQ Connect 构建数据流转处理平台

作者&#xff1a;周波&#xff0c;阿里云智能高级开发工程师&#xff0c; Apache RocketMQ Committer 01 从问题中来的 RocketMQ Connect 在电商系统、金融系统及物流系统&#xff0c;我们经常可以看到 RocketMQ 的身影。原因不难理解&#xff0c;随着数字化转型范围的扩大及…

Linux学习笔记——多线程

文章目录补充知识Linux线程概念线程的优点线程的缺点线程异常线程用途多进程的应用场景Linux进程VS线程重新理解进程进程和线程线程共享的进程资源和环境为什么线程切换的成本更低进程和线程的关系Linux线程控制POSIX线程库创建线程线程ID及进程地址空间布局线程终止线程等待线…

pycharm常用的插件

pycharm等IDE 无法编辑bat文件 Chinese Language Pack 汉化插件 vim CodeGlance 右侧地图 将类似于Sublime中的代码小地图嵌入到编辑器窗格中。使用自定义颜色进行语法高亮&#xff0c;同时使用明暗主题。 Rainbow Brackets&#xff08;必备推荐&#xff09;收费了 这个插件…

【数据库原理 • 六】数据库备份与恢复

前言 数据库技术是计算机科学技术中发展最快&#xff0c;应用最广的技术之一&#xff0c;它是专门研究如何科学的组织和存储数据&#xff0c;如何高效地获取和处理数据的技术。它已成为各行各业存储数据、管理信息、共享资源和决策支持的最先进&#xff0c;最常用的技术。 当前…

Baumer工业相机堡盟工业相机中预处理相机的特性优势以及行业应用

Baumer工业相机堡盟工业相机如何通过BGAPISDK里显示彩色相机和黑白相机的图像&#xff08;C#&#xff09;Baumer工业相机Baumer工业相机的预处理相机的技术背景Baumer工业相机中预处理相机的特性Baumer工业相机中图像压缩相机的特性Baumer工业相机中3D激光相机的特性Baumer工业…

ESP32设备驱动-MAX30102脉搏血氧饱和度和心率监测传感器驱动

MAX30102脉搏血氧饱和度和心率监测传感器驱动 文章目录 MAX30102脉搏血氧饱和度和心率监测传感器驱动1、MAX30102介绍2、硬件准备3、软件准备4、驱动实现1、MAX30102介绍 MAX30102是一款集成脉搏血氧饱和度和心率监测生物传感器模块。 它包括内部 LED、光电探测器、光学元件和…

网页滚动体验,IScroll滚动插件,你安装了类似的滚动页面插件吗

IScroll是一款基于JavaScript的插件&#xff0c;用于在网页中实现平滑滚动效果。 这个插件可以帮助用户创建回到页面顶部和底部的按钮、生成页面导航快照&#xff0c;以及设置滚动时间等功能&#xff0c;从而提升网页的用户体验。 IScroll的特点在于&#xff0c;它能够平滑地…

【15】数据操作reshape、张量的运算

1. N维数组 ① 机器学习用的最多的是N维数组&#xff0c;N维数组是机器学习和神经网络的主要数据结构。 2. 创建数组 ① 创建数组需要&#xff1a;形状、数据类型、元素值。 3. 访问元素 ① 可以根据切片&#xff0c;或者间隔步长访问元素。 ② [::3,::2]是每隔3行、2列访问…

考研数据结构--线性表

线性表 文章目录线性表概述线性表的特点线性表的基本操作线性表的顺序表示概述优缺点操作顺序表的定义顺序表的初始化顺序表的插入顺序表的删除顺序表的查找顺序表的输出顺序表的判空顺序表的销毁main方法测试线性表的链式表示概述优缺点单链表操作单链表的定义单链表的初始化单…

自动化面试题3

1、PLC输入输出如何接线&#xff0c;源性漏型如何看&#xff1f; 所谓“源型输入”&#xff0c;是指电流从模块的公共端流入&#xff0c;从模块的输入通道流出的接线方式。源型输入的公共端作为电源正极&#xff08;共阳极&#xff09;。源型输入可以等效为在输入模块外部连接…

C# | 上位机开发新手指南(九)加密算法——RSA

上位机开发新手指南&#xff08;九&#xff09;加密算法——RSA 文章目录上位机开发新手指南&#xff08;九&#xff09;加密算法——RSA前言RSA的特性非对称性安全性可逆性签名速度较慢密钥管理RSA算法的参数公钥公钥指数e模数n私钥私钥指数d模数n质数p和qdp和dqqInv质数填充方…