Java:SpringBoot使用AES对JSON数据加密和解密
目录
- 1、加密解密原理
- 2、项目示例
- 2.1、项目结构
- 2.2、常规业务代码
- 2.3、加密的实现
- 2.4、接口测试
- 2.5、总结
1、加密解密原理
客户端和服务端都可以加密和解密,使用base64进行网络传输
加密方
字符串 -> AES加密 -> base64
解密方
base64 -> AES解密 -> 字符串
2、项目示例
2.1、项目结构
$ tree -I target -I test
.
├── pom.xml
└── src└── main├── java│ └── com│ └── example│ └── demo│ ├── Application.java│ ├── annotation│ │ └── SecretData.java│ ├── config│ │ ├── CrossConfig.java│ │ ├── DecryptRequestBodyAdvice.java│ │ ├── EncryptResponseBodyAdvice.java│ │ ├── SecretConfig.java│ │ └── WebMvcConfig.java│ ├── controller│ │ ├── IndexController.java│ │ └── UserController.java│ ├── request│ │ └── JsonRequest.java│ ├── response│ │ ├── JsonResult.java│ │ └── JsonResultVO.java│ ├── service│ │ ├── SecretDataService.java│ │ └── impl│ │ └── SecretDataServiceImpl.java│ └── utils│ └── CipherUtil.java└── resources├── application.yml├── static│ ├── axios.min.js│ └── crypto-js.min.js└── templates└── index.html
2.2、常规业务代码
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.7</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>demo</artifactId><version>0.0.1-SNAPSHOT</version><name>demo</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version><mybatis-plus.version>3.5.2</mybatis-plus.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId><version>1.15</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><!-- test --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>
Application.java
package com.example.demo;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}
WebMvcConfig.java
package com.example.demo.config;import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;@Slf4j
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {// 设置静态资源映射@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");}
}
CrossConfig.java
package com.example.demo.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;/*** 处理跨域问题*/
@Configuration
public class CrossConfig {@Beanpublic CorsFilter corsFilter() {CorsConfiguration config = new CorsConfiguration();config.addAllowedOrigin("*");config.setAllowCredentials(false);config.addAllowedMethod("*");config.addAllowedHeader("*");config.setMaxAge(3600L);UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();configSource.registerCorsConfiguration("/**", config);return new CorsFilter(configSource);}
}
JsonRequest.java
package com.example.demo.request;import lombok.Data;/*** 统一的请求体数据*/
@Data
public class JsonRequest {/*** 未加密数据*/private Object data;/*** 加密数据*/private String encryptData;
}
JsonResult.java
package com.example.demo.response;import lombok.Data;/*** 统一的返回体数据 不加密*/
@Data
public class JsonResult<T> {private String message;private T data;private Integer code;public static <T> JsonResult success(T data){JsonResult<T> jsonResult = new JsonResult<>();jsonResult.setCode(0);jsonResult.setData(data);jsonResult.setMessage("success");return jsonResult;}
}
JsonResultVO.java
package com.example.demo.response;import lombok.Data;/*** 统一的返回体数据 加密*/
@Data
public class JsonResultVO {private String message;private String encryptData;private Integer code;
}
IndexController.java
package com.example.demo.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;@Controller
public class IndexController {@GetMapping("/")public String index(){return "index";}
}
UserController.java
package com.example.demo.controller;import com.example.demo.annotation.SecretData;
import com.example.demo.response.JsonResult;
import org.springframework.web.bind.annotation.*;import java.util.HashMap;
import java.util.Map;/*** 对该Controller中的所有方法进行加解密处理*/
@RestController
@SecretData
public class UserController {@GetMapping("/user/getUser")public JsonResult getUser() {Map<String, String> user = new HashMap<>();user.put("name", "Tom");user.put("age", "18");return JsonResult.success(user);}@PostMapping("/user/addUser")public Object addUser(@RequestBody Map<String, String> data) {System.out.println(data);return data;}
}
2.3、加密的实现
application.yml
secret:key: 1234567890123456 # 密钥位数为16位enabled: true # 开启加解密功能
SecretData.java
package com.example.demo.annotation;import org.springframework.web.bind.annotation.Mapping;import java.lang.annotation.*;/*** 只有添加有该注解的Controller类或是具体接口方法才进行数据的加密解密*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Mapping
@Documented
public @interface SecretData {}
DecryptRequestBodyAdvice.java
package com.example.demo.config;import com.example.demo.annotation.SecretData;
import com.example.demo.request.JsonRequest;
import com.example.demo.service.SecretDataService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;/*** 对请求内容进行解密* 只有开启了加解密功能才会生效* 仅对使用了@RqestBody注解的生效* https://blog.csdn.net/xingbaozhen1210/article/details/98189562*/
@ControllerAdvice
// @ConditionalOnProperty(name = "secret.enabled", havingValue = "true")
public class DecryptRequestBodyAdvice extends RequestBodyAdviceAdapter {@Resourceprivate SecretDataService secretDataService;@Overridepublic boolean supports(MethodParameter methodParameter, Type targetType,Class<? extends HttpMessageConverter<?>> converterType) {return methodParameter.getMethod().isAnnotationPresent(SecretData.class)|| methodParameter.getMethod().getDeclaringClass().isAnnotationPresent(SecretData.class);}@Overridepublic HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,Class<? extends HttpMessageConverter<?>> converterType) throws IOException {System.out.println("beforeBodyRead");String body = inToString(inputMessage.getBody());System.out.println(body);ObjectMapper objectMapper = new ObjectMapper();JsonRequest jsonRequest = objectMapper.readValue(body, JsonRequest.class);// 默认取data数据,如果提交加密数据则解密String decryptData = null;if (jsonRequest.getEncryptData() != null) {decryptData = secretDataService.decrypt(jsonRequest.getEncryptData());} else{decryptData = objectMapper.writeValueAsString(jsonRequest.getData());}String data = decryptData;// 解密后的数据System.out.println(data);return new HttpInputMessage() {@Overridepublic HttpHeaders getHeaders() {return inputMessage.getHeaders();}@Overridepublic InputStream getBody() throws IOException {return new ByteArrayInputStream(data.getBytes());}};}/*** 读取输入流为字符串** @param is* @return*/private String inToString(InputStream is) {byte[] buf = new byte[10 * 1024];int length = -1;StringBuilder sb = new StringBuilder();try {while ((length = is.read(buf)) != -1) {sb.append(new String(buf, 0, length));}return sb.toString();} catch (IOException e) {throw new RuntimeException(e);}}}
EncryptResponseBodyAdvice.java
package com.example.demo.config;import com.example.demo.annotation.SecretData;
import com.example.demo.response.JsonResult;
import com.example.demo.response.JsonResultVO;
import com.example.demo.service.SecretDataService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;import javax.annotation.Resource;/*** 对响应内容加密*/
@ControllerAdvice
@ConditionalOnProperty(name = "secret.enabled", havingValue = "true")
public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Object> {@Resourceprivate SecretDataService secretDataService;@Overridepublic boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {return returnType.getMethod().isAnnotationPresent(SecretData.class)|| returnType.getMethod().getDeclaringClass().isAnnotationPresent(SecretData.class);}@SneakyThrows@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType,ServerHttpRequest request, ServerHttpResponse response) {System.out.println("beforeBodyWrite");// 仅对JsonResult对象数据加密if (body instanceof JsonResult) {JsonResult jsonResult = (JsonResult) body;JsonResultVO jsonResultVO = new JsonResultVO();BeanUtils.copyProperties(jsonResult, jsonResultVO);String jsonStr = new ObjectMapper().writeValueAsString(jsonResult.getData());jsonResultVO.setEncryptData(secretDataService.encrypt(jsonStr));return jsonResultVO;} else {return body;}}
}
SecretConfig.java
package com.example.demo.config;import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;@Configuration
@ConfigurationProperties(prefix = "secret")
public class SecretConfig {private Boolean enabled;private String key;public Boolean getEnabled() {return enabled;}public void setEnabled(Boolean enabled) {this.enabled = enabled;}public String getKey() {return key;}public void setKey(String key) {this.key = key;}}
SecretDataService.java
package com.example.demo.service;/*** 加密解密的接口*/
public interface SecretDataService {/*** 数据加密** @param data 待加密数据* @return String 加密结果*/String encrypt(String data);/*** 数据解密** @param data 待解密数据* @return String 解密后的数据*/String decrypt(String data);
}
SecretDataServiceImpl.java
package com.example.demo.service.impl;import com.example.demo.config.SecretConfig;
import com.example.demo.service.SecretDataService;
import com.example.demo.utils.CipherUtil;
import org.springframework.stereotype.Service;import javax.annotation.Resource;/*** 具体的加解密实现*/
@Service
public class SecretDataServiceImpl implements SecretDataService {@Resourceprivate SecretConfig secretConfig;@Overridepublic String decrypt(String data) {return CipherUtil.decrypt(secretConfig.getKey(), data);}@Overridepublic String encrypt(String data) {return CipherUtil.encrypt(secretConfig.getKey(), data);}
}
CipherUtil.java
package com.example.demo.utils;import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.util.Base64;/*** 数据加密解密工具类* 加密后返回base64*/
public class CipherUtil {/*** 定义加密算法*/private static final String ALGORITHM = "AES/ECB/PKCS5Padding";/*** 解密** @param secretKey* @param cipherText base64* @return*/public static String decrypt(String secretKey, String cipherText) {// 将Base64编码的密文解码byte[] encrypted = Base64.getDecoder().decode(cipherText);try {Cipher cipher = Cipher.getInstance(ALGORITHM);SecretKeySpec key = new SecretKeySpec(secretKey.getBytes(), "AES");cipher.init(Cipher.DECRYPT_MODE, key);return new String(cipher.doFinal(encrypted));} catch (Exception e) {throw new RuntimeException(e);}}/*** 加密** @param secretKey* @param plainText base64* @return*/public static String encrypt(String secretKey, String plainText) {try {Cipher cipher = Cipher.getInstance(ALGORITHM);SecretKeySpec key = new SecretKeySpec(secretKey.getBytes(), "AES");cipher.init(Cipher.ENCRYPT_MODE, key);return Base64.getEncoder().encodeToString(cipher.doFinal(plainText.getBytes(Charset.forName("UTF-8"))));} catch (Exception e) {throw new RuntimeException(e);}}
}
浏览器中实现加密解密
templates/index.html
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org"><head><meta charset="UTF-8" /><title>接口数据加密解密</title></head><body><!-- 引入依赖 --><!-- <script src="https://cdn.bootcdn.net/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script> --><script src="/static/crypto-js.min.js"></script><!-- <script src="https://cdn.bootcdn.net/ajax/libs/axios/1.3.6/axios.min.js"></script> --><script src="/static/axios.min.js"></script><h1>查看控制台</h1><!-- 加密解密模块 --><script type="text/javascript">const SECRET_KEY = "1234567890123456";/*** 加密方法* @param data 待加密数据* @returns {string|*}*/function encrypt(data) {let key = CryptoJS.enc.Utf8.parse(SECRET_KEY);if (typeof data === "object") {data = JSON.stringify(data);}let plainText = CryptoJS.enc.Utf8.parse(data);let secretText = CryptoJS.AES.encrypt(plainText, key, {mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7}).toString();return secretText;}/*** 解密数据* @param data 待解密数据*/function decrypt(data) {let key = CryptoJS.enc.Utf8.parse(SECRET_KEY);let result = CryptoJS.AES.decrypt(data, key, {mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7}).toString(CryptoJS.enc.Utf8);return JSON.parse(result);}</script><!-- http请求模块 --><script type="text/javascript">// 获取加密数据并解密axios.get("http://127.0.0.1:8080/user/getUser").then((res) => {console.log("接收到api返回加密数据:");console.log(decrypt(res.data.encryptData));});// 提交加密参数const data = {name: "Tom",age: "18",};axios.post("http://127.0.0.1:8080/user/addUser", {encryptData: encrypt(data),}).then((res) => {console.log("接收到api返回未加密数据:");console.log(res.data);});</script></body>
</html>
2.4、接口测试
- 开发环境不加密更易于开发调试
- 生产环境需要数据加密
前后端都可以通过参数 encryptData
判断对方提交/返回的数据是否为加密数据,如果是加密数据则进行解密操作
接口返回加密数据
GET http://127.0.0.1:8080/user/getUser
未加密的数据
{"message": "success","data": {"name": "Tom","age": "18"},"code": 0
}
返回数据
{"message": "success","encryptData": "kun2Wvk2LNKICaXIeIExA7jKRyqOV0qCv5KQXFOzfpQ=","code": 0
}
客户端提交参数
POST http://127.0.0.1:8080/user/addUser
提交数据
{"data": {"name": "Tom","age": "18"}
}
提交加密数据
{"encryptData": "kun2Wvk2LNKICaXIeIExA7jKRyqOV0qCv5KQXFOzfpQ="
}
2.5、总结
服务端
- 全局开关:通过控制
secret.enabled=true
全局开启返回数据加密 - 全局局部:可以通过
SecretData
或者自定义PassSecretData
来控制单个控制器或者单个接口的需要或不需要加密
客户端
- 可以根据开发环境、测试环境、生产环境来控制是否开启加密
- 需要注意,FormData传输文件的数据格式可以考虑不加密
相同点
- 服务端和客户端都通过对方传输的
encryptData
来判断是否为加密数据 - 服务端和客户端都可以根据自己的环境来决定是否开启数据加密
完整代码:https://github.com/mouday/spring-boot-demo/tree/master/SpringBoot-Secret
参考文章
- 详解API接口如何安全的传输数据
相关文章:
Java:SpringBoot使用AES对JSON数据加密和解密
目录 1、加密解密原理2、项目示例2.1、项目结构2.2、常规业务代码2.3、加密的实现 2.4、接口测试2.5、总结 1、加密解密原理 客户端和服务端都可以加密和解密,使用base64进行网络传输 加密方 字符串 -> AES加密 -> base64解密方 base64 -> AES解密 -&g…...
【PHP】Swoole:一款强大的PHP网络编程工具
在科学计算领域,Swoole是一款功能强大的PHP扩展,它提供了高性能的网络通信和异步编程功能。Swoole不仅支持TCP、UDP、Unix Socket和HTTP等通信协议,还具有异步并发处理能力,使得PHP开发者能够轻松地构建高性能的网络应用程序。 1…...

【C语言】每日一题(除自身以外数组的乘积)
添加链接描述,链接奉上 方法: 暴力循环:前缀积后缀积(分组): 暴力循环: 暴力循换真的是差生法宝,简单好懂,就是不实用,大多数的题目都会超过时间限制(无奈) 思路&…...

C语言每日一练-----Day(4)
本专栏为c语言练习专栏,适合刚刚学完c语言的初学者。本专栏每天会不定时更新,通过每天练习,进一步对c语言的重难点知识进行更深入的学习。 今日练习题关键字:记负均正 旋转数组的最小数字 二分查找 💓博主…...

如何使用工具将批量查询的物流信息导出到表格
现如今,物流行业发展迅速,人们对于物流信息的查询需求也越来越高。为了满足用户的需求,我们推荐一款便捷高效的物流信息查询工具——"固乔快递查询助手"软件。 首先,用户需要下载并安装"固乔快递查询助手"软件…...

Haproxy+Keepalive 整合rabbitmq实现高可用负载均衡
Haproxy 实现负载均衡 HAProxy 提供高可用性、负载均衡及基于 TCPHTTP 应用的代理,支持虚拟主机,它是免费、快速并且可靠的一种解决方案,包括 Twitter,Reddit,StackOverflow,GitHub 在内的多家知名互联网公司在使用。HAProxy 实现了一种…...
电子病历系统的核心技术——电子病历编辑器
一体化电子病历系统基于云端SaaS服务的方式,采用B/S(Browser/Server)架构提供,覆盖了医疗机构电子病历模板制作到管理使用的整个流程。除实现在线制作内容丰富、图文并茂、功能完善的电子病历模板外,还可按照医疗机构的…...

C++------map和set的使用
文章目录 关联式容器键值对树型结构的关联式容器set的介绍map的介绍 关联式容器 什么是关联式容器?它与序列式容器有什么区别? 关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key,value>结…...

URI和URL和URN区别
URI、URL 和 URN 是一系列从不同角度来看待资源标识和定位的概念。虽然它们有一些重叠,但每个概念都强调了不同的方面。 URI(Uniform Resource Identifier):URI 是一个通用的术语,用于标识和定位资源。它是一个抽象的概…...

【Unity学习笔记】DOTween(1)基础介绍
本文中大部分内容学习来自DOTween官方文档 文章目录 什么是DOTween?DOSetOnTweenerSequenceTweenNested tween 初始化使用方式 什么是DOTween? DOTween是一个动画插件,Tween是补间的意思。这个插件以下简称DOT,DOT很方便使用&…...
springboot项目,使用JNA框架调用C++库无法捕获异常的解决思路
写在前面:这个东西真的坑,工作上遇到的和JNA项目的入门案例差别就像是二重积分与小学数学之间的差距,折磨! 使用 JNA(Java Native Access)框架调用 C 库时,如果出现问题导致进程直接结束而无法捕…...

【项目 计网7】4.20 多进程实现并发服务器 4.22 多线程实现并发服务器
文章目录 4.20 多进程实现并发服务器server_process.cclient.c4.22 多线程实现并发服务器客户端代码:服务端代码: 4.20 多进程实现并发服务器 要实现TCP通信服务器处理并发的任务,使用多线程或者多进程来解决。 思路: 1、一个父进…...

论文阅读_扩散模型_LDM
英文名称: High-Resolution Image Synthesis with Latent Diffusion Models 中文名称: 使用潜空间扩散模型合成高分辨率图像 地址: https://ieeexplore.ieee.org/document/9878449/ 代码: https://github.com/CompVis/latent-diffusion 作者:Robin Rombach 日期: 20…...
LeetCode——回溯篇(二)
刷题顺序及思路来源于代码随想录,网站地址:https://programmercarl.com 目录 131. 分割回文串 93. 复原 IP 地址 78. 子集 90. 子集 II 491. 递增子序列 131. 分割回文串 给你一个字符串 s,请你将 s 分割成一些子串,使每个…...

RabbitMQ工作模式-发布订阅模式
Publish/Subscribe(发布订阅模式) 官方文档: https://www.rabbitmq.com/tutorials/tutorial-three-python.html 使用fanout类型类型的交换器,routingKey忽略。每个消费者定义生成一个队列关绑定到同一个Exchange,每个…...

JDK源码解析-Object
1. Object类 所有类的基类——java.lang.Object Object 类是所有类的基类,当一个类没有直接继承某个类时,默认继承Object类Object 类属于 java.lang 包,此包下的所有类在使用时无需手动导入,系统会在程序编译期间自动导入。 思…...

pinia——添加插件——基础积累
问题:是否给pinia添加过插件?具体添加的方式是什么? 在pinia中,我们可以为仓库添加插件,通过添加插件能够扩展以下的内容: 为 store 添加新的属性 定义 store 时增加新的选项 为 store 增加新的方法 包装现…...
软件国产化之殇
今天又看到这么一个帖子讨论一款国产化软件,属实给我震撼到了。 对于国产化产品,一直主打的都是”自研“,难道是我对”自研“这个词的理解有误? 做一个产品,别人开源了,你拿过来使用,你可以说…...
SQLyog问题处理集合
sqlyog 问题处理 1. 错误号码:1049错误: 数据库命令参数参考:数据库命令地址 检查数据库是否存在检查创建的数据库名称 与 要进行连接的数据库名称是否一致; 2. 错误号码:1819错误: MySQL授予远程连接权限时出现: …...

JavaSE【继承和多态】(1)(重点:初始化、pretected封装、组合)
一、继承 继承 (inheritance) 机制 :是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性 的基础上进行扩展,增加新功能 ,这样产生新的类,称 派生类 。 继承呈现了面向对象程序设计的层次结…...

网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...
MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释
以Module Federation 插件详为例,Webpack.config.js它可能的配置和含义如下: 前言 Module Federation 的Webpack.config.js核心配置包括: name filename(定义应用标识) remotes(引用远程模块࿰…...