【Mybatis-Plus】根据自定义注解实现自动加解密
背景
我们把数据存到数据库的时候,有些敏感字段是需要加密的,从数据库查出来再进行解密。如果存在多张表或者多个地方需要对部分字段进行加解密操作,每个地方都手写一次加解密的动作,显然不是最好的选择。如果我们使用的是Mybatis
框架,那就跟着一起探索下如何使用框架的拦截器功能实现自动加解密吧。
定义一个自定义注解
我们需要一个注解,只要实体类的属性加上这个注解,那么就对这个属性进行自动加解密。我们把这个注解定义灵活一点,不仅可以放在属性上,还可以放到类上,如果在类上使用这个注解,代表这个类的所有属性都进行自动加密。
/*** 加密字段*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
public @interface EncryptField {}
定义实体类
package com.wen3.demo.mybatisplus.po;import com.baomidou.mybatisplus.annotation.*;
import com.wen3.demo.mybatisplus.encrypt.annotation.EncryptField;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;@EncryptField
@Getter
@Setter
@Accessors(chain = true)
@KeySequence(value = "t_user_user_id_seq", dbType = DbType.POSTGRE_SQL)
@TableName("t_USER")
public class UserPo {/*** 用户id*/@TableId(value = "USER_ID", type = IdType.INPUT)private Long userId;/*** 用户姓名*/@TableField("USER_NAME")private String userName;/*** 用户性别*/@TableField("USER_SEX")private String userSex;/*** 用户邮箱*/@EncryptField@TableField("USER_EMAIL")private String userEmail;/*** 用户账号*/@TableField("USER_ACCOUNT")private String userAccount;/*** 用户地址*/@TableField("USER_ADDRESS")private String userAddress;/*** 用户密码*/@TableField("USER_PASSWORD")private String userPassword;/*** 用户城市*/@TableField("USER_CITY")private String userCity;/*** 用户状态*/@TableField("USER_STATUS")private String userStatus;/*** 用户区县*/@TableField("USER_SEAT")private String userSeat;
}
拦截器
Mybatis-Plus
有个拦截器接口com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor
,但发现这个接口有一些不足
- 必须构建一个
MybatisPlusInterceptor
这样的Bean - 并调用这个
Bean
的addInnerInterceptor
方法,把所有的InnerInterceptor
加入进去,才能生效 InnerInterceptor
只有before
拦截,缺省after
拦截。加密可以在before
里面完成,但解密需要在after
里面完成,所以这个InnerInterceptor
不能满足我们的要求
所以继续研究源码,发现Mybatis
有个org.apache.ibatis.plugin.Interceptor
接口,这个接口能满足我对自动加解密的所有诉求
- 首先,实现
Interceptor
接口,只要注册成为Spring
容器的Bean
,拦截器就能生效 - 可以更加灵活的在
before
和after
之间插入自己的逻辑
加密拦截器
创建名为EncryptInterceptor
的加密拦截器,对update
操作进行拦截,对带@EncryptField
注解的字段进行加密处理,无论是save
方法还是saveBatch
方法都会被成功拦截到。
package com.wen3.demo.mybatisplus.encrypt.interceptor;import com.wen3.demo.mybatisplus.encrypt.annotation.EncryptField;
import com.wen3.demo.mybatisplus.encrypt.util.FieldEncryptUtil;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.Objects;/*** 对update操作进行拦截,对{@link EncryptField}字段进行加密处理;* 无论是save方法还是saveBatch方法都会被成功拦截;*/
@Slf4j
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
@Component
public class EncryptInterceptor implements Interceptor {private static final String METHOD = "update";@Setter(onMethod_ = {@Autowired})private FieldEncryptUtil fieldEncryptUtil;@Overridepublic Object intercept(Invocation invocation) throws Throwable {if(!StringUtils.equals(METHOD, invocation.getMethod().getName())) {return invocation.proceed();}// 根据update拦截规则,第0个参数一定是MappedStatement,第1个参数是需要进行判断的参数Object param = invocation.getArgs()[1];if(Objects.isNull(param)) {return invocation.proceed();}// 加密处理fieldEncryptUtil.encrypt(param);return invocation.proceed();}
}
解密拦截器
创建名为DecryptInterceptor
的加密拦截器,对query
操作进行拦截,对带@EncryptField
注解的字段进行解密处理,无论是返回单个对象,还是对象的集合,都会被拦截到。
package com.wen3.demo.mybatisplus.encrypt.interceptor;import cn.hutool.core.util.ClassUtil;
import com.wen3.demo.mybatisplus.encrypt.annotation.EncryptField;
import com.wen3.demo.mybatisplus.encrypt.util.FieldEncryptUtil;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.sql.Statement;
import java.util.Collection;/*** 对query操作进行拦截,对{@link EncryptField}字段进行解密处理;*/
@Slf4j
@Intercepts({@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = Statement.class)
})
@Component
public class DecryptInterceptor implements Interceptor {private static final String METHOD = "query";@Setter(onMethod_ = {@Autowired})private FieldEncryptUtil fieldEncryptUtil;@SuppressWarnings("rawtypes")@Overridepublic Object intercept(Invocation invocation) throws Throwable {Object result = invocation.proceed();// 解密处理// 经过测试发现,无论是返回单个对象还是集合,result都是ArrayList类型if(ClassUtil.isAssignable(Collection.class, result.getClass())) {fieldEncryptUtil.decrypt((Collection) result);} else {fieldEncryptUtil.decrypt(result);}return result;}
}
加解密工具类
由于加密和解密绝大部分的逻辑是相似的,不同的地方在于
- 加密需要通过反射处理的对象,是在
SQL
执行前,是Invocation
对象的参数列表中下标为1
的参数;而解决需要通过反射处理的对象,是在SQL
执行后,对执行结果对象进行解密处理。 - 一个是获取到字段值进行加密,一个是获取到字段值进行解密
于是把加解密逻辑抽象成一个工具类,把差异的部分做为参数传入
package com.wen3.demo.mybatisplus.encrypt.util;import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import com.wen3.demo.mybatisplus.encrypt.annotation.EncryptField;
import com.wen3.demo.mybatisplus.encrypt.service.FieldEncryptService;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import java.util.Objects;/*** 加解密工具类*/
@Slf4j
@Component
public class FieldEncryptUtil {@Setter(onMethod_ = {@Autowired})private FieldEncryptService fieldEncryptService;/**对EncryptField注解进行加密处理*/public void encrypt(Object obj) {if(ClassUtil.isPrimitiveWrapper(obj.getClass())) {return;}encryptOrDecrypt(obj, true);}/**对EncryptField注解进行解密处理*/public void decrypt(Object obj) {encryptOrDecrypt(obj, false);}/**对EncryptField注解进行解密处理*/public void decrypt(Collection list) {if(CollectionUtils.isEmpty(list)) {return;}list.forEach(this::decrypt);}/**对EncryptField注解进行加解密处理*/private void encryptOrDecrypt(Object obj, boolean encrypt) {// 根据update拦截规则,第0个参数一定是MappedStatement,第1个参数是需要进行判断的参数if(Objects.isNull(obj)) {return;}// 获取所有带加密注解的字段List<Field> encryptFields = null;// 判断类上面是否有加密注解EncryptField encryptField = AnnotationUtils.findAnnotation(obj.getClass(), EncryptField.class);if(Objects.nonNull(encryptField)) {// 如果类上有加密注解,则所有字段都需要加密encryptFields = FieldUtils.getAllFieldsList(obj.getClass());} else {encryptFields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), EncryptField.class);}// 没有字段需要加密,则跳过if(CollectionUtils.isEmpty(encryptFields)) {return;}encryptFields.forEach(f->{// 只支持String类型的加密if(!ClassUtil.isAssignable(String.class, f.getType())) {return;}String oldValue = (String) ReflectUtil.getFieldValue(obj, f);if(StringUtils.isBlank(oldValue)) {return;}String logText = null, newValue = null;if(encrypt) {logText = "encrypt";newValue = fieldEncryptService.encrypt(oldValue);} else {logText = "decrypt";newValue = fieldEncryptService.decrypt(oldValue);}log.info("{} success[{}=>{}]. before:{}, after:{}", logText, f.getDeclaringClass().getName(), f.getName(), oldValue, newValue);ReflectUtil.setFieldValue(obj, f, newValue);});}
}
加解密算法
Mybatis-Plus
自带了一个AES
加解密算法的工具,我们只需要提供一个加密key
,然后就可以完成一个加解密的业务处理了。
- 先定义一个加解密接口
package com.wen3.demo.mybatisplus.encrypt.service;/*** 数据加解密接口*/
public interface FieldEncryptService {/**对数据进行加密*/String encrypt(String value);/**对数据进行解密*/String decrypt(String value);/**判断数据是否忆加密*/default boolean isEncrypt(String value) {return false;}
}
- 然后实现一个默认的加解密实现类
package com.wen3.demo.mybatisplus.encrypt.service.impl;import cn.hutool.core.util.ClassUtil;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.AES;
import com.wen3.demo.mybatisplus.encrypt.service.FieldEncryptService;
import org.springframework.stereotype.Component;import javax.crypto.IllegalBlockSizeException;/*** 使用Mybatis-Plus自带的AES加解密*/
@Component
public class DefaultFieldEncryptService implements FieldEncryptService {private static final String ENCRYPT_KEY = "abcdefghijklmnop";@Overridepublic String encrypt(String value) {if(isEncrypt(value)) {return value;}return AES.encrypt(value, ENCRYPT_KEY);}@Overridepublic String decrypt(String value) {return AES.decrypt(value, ENCRYPT_KEY);}@Overridepublic boolean isEncrypt(String value) {// 判断是否已加密try {// 解密成功,说明已加密decrypt(value);return true;} catch (MybatisPlusException e) {if(ClassUtil.isAssignable(IllegalBlockSizeException.class, e.getCause().getClass())) {return false;}throw e;}}
}
自动加解密单元测试
package com.wen3.demo.mybatisplus.service;import cn.hutool.core.util.RandomUtil;
import com.wen3.demo.mybatisplus.MybatisPlusSpringbootTestBase;
import com.wen3.demo.mybatisplus.encrypt.service.FieldEncryptService;
import com.wen3.demo.mybatisplus.po.UserPo;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.Test;import java.util.Collections;
import java.util.List;
import java.util.Map;class UserServiceTest extends MybatisPlusSpringbootTestBase {@Resourceprivate UserService userService;@Resourceprivate FieldEncryptService fieldEncryptService;@Testvoid save() {UserPo userPo = new UserPo();String originalValue = RandomStringUtils.randomAlphabetic(16);String encryptValue = fieldEncryptService.encrypt(originalValue);userPo.setUserEmail(originalValue);userPo.setUserName(RandomStringUtils.randomAlphabetic(16));boolean testResult = userService.save(userPo);assertTrue(testResult);assertNotEquals(originalValue, userPo.getUserEmail());assertEquals(encryptValue, userPo.getUserEmail());// 测试解密: 返回单个对象UserPo userPoQuery = userService.getById(userPo.getUserId());assertEquals(originalValue, userPoQuery.getUserEmail());// 测试解密: 返回ListList<UserPo> userPoList = userService.listByEmail(encryptValue);assertEquals(originalValue, userPoList.get(0).getUserEmail());// 测试saveBatch方法也会被拦截加密userPo.setUserId(null);testResult = userService.save(Collections.singletonList(userPo));assertTrue(testResult);assertNotEquals(originalValue, userPo.getUserEmail());assertEquals(encryptValue, userPo.getUserEmail());}
}
单元测试运行截图
相关文章:
![](https://img-blog.csdnimg.cn/direct/d493bfe16b1f40c7b4a6328d623b858d.png)
【Mybatis-Plus】根据自定义注解实现自动加解密
背景 我们把数据存到数据库的时候,有些敏感字段是需要加密的,从数据库查出来再进行解密。如果存在多张表或者多个地方需要对部分字段进行加解密操作,每个地方都手写一次加解密的动作,显然不是最好的选择。如果我们使用的是Mybati…...
![](https://www.ngui.cc/images/no-images.jpg)
Window上ubuntu子系统编译Android
Window上ubuntu子系统编译Android 1、编译环境2、WSL2编译报错2.1 You are building on a machine with 11.6GB of RAM2.2 Case-insensitive filesystems not supported3. android模拟器调试 1、编译环境 AOSP : Android源码下载安装java:sudo apt-get install ope…...
![](https://img-blog.csdnimg.cn/direct/8d4488aac7ee4a879ecfa8f0895b3f7c.png)
【Java学习笔记】异常处理
生活中我们在使用一些产品的时候,经常会碰到一些异常情况。例如,使用ATM机取钱的时,机器会突然出现故障导致无法完成正常的取钱业务,甚至吞卡;在乘坐地铁时,地铁出现异常无法按时启动和运行;使用…...
![](https://www.ngui.cc/images/no-images.jpg)
Ubuntu20.04环境下Baxter机器人开发环境搭建
Ubuntu20.04环境下Baxter机器人开发环境搭建 ubuntu20.04安装 略 安装ROS 略 Baxter机器人依赖安装 主目录创建工作空间,按以下步骤执行 mkdir -p ~/baxter_ws/src source /opt/ros/noetic/setup.bash cd ~/baxter_ws catkin_make catkin_make install s…...
![](https://img-blog.csdnimg.cn/direct/8f17698c2e0442f7995f42c8e4479259.png)
nccl 03 记 回顾:从下载,编译到调试 nccl-test
1, 下载与编译 1.1 源码下载 $ git clone https://github.com/NVIDIA/nccl.git 1.2 编译 1.2.1 一般编译: $ make -j src.build 1.2.2 特定架构gpu 编译 $ make -j src.build NVCC_GENCODE"-gencodearchcompute_80,codesm_80" A10…...
![](https://img-blog.csdnimg.cn/img_convert/da762937bc1acfc02637545a96a6c735.png)
关于车规级功率器件热可靠性测试的分享
随着中国电动汽车市场的稳步快速发展和各大车企布局新能源的扩散,推动了车规级功率器件的快速增长。新能源汽车行业和消费电子都会用到半导体芯片,但车规级芯片对外部环境要求很高,涉及到的一致性和可靠性均要大于工业级产品要求,…...
![](https://www.ngui.cc/images/no-images.jpg)
内核学习——1、list_head
双向循环链表:list_head 头节点head是不使用的: struct list_head { struct list_head *next, *prev; }; 结构体中没有数据域,所以一般把list_head嵌入到其他结构中使用 struct file_node { char c; struct list_head node; }; 此时ÿ…...
![](https://www.ngui.cc/images/no-images.jpg)
JavaEE初阶--网络基本概念
目录 一、引言 二、网络基本概念 2.1 局域网LAN 2.2 广域网WAN 三、网络通信的基础 3.1 IP地址 3.2 端口号 3.3 协议 3.4 五元组 3.5 协议分层 3.6 OSI七层模型 3.7 TCP/IP五层模型 四、总结 一、引言 本篇博客将进入网络编程以及网络原理的学习,但网…...
![](https://img-blog.csdnimg.cn/direct/1f7a3e0eeb7b401cb9f10580a9c569ce.png)
gitlab-cicd-k8s
k8s已经准备好 kubectl get node 创建cicdYaml文件 kubectl create namespace gitlab-cicd --dry-runclient --outputyaml >> gitlab-cicd.yaml kubectl apply -f gitlab-cicd.yaml 服务器和仓库在一起可用专有地址 使用 GitLab Runner 可以自动执行 GitLab CI/CD 管道…...
![](https://img-blog.csdnimg.cn/img_convert/b71bf37bfc63e2182e899a5988d026f1.png)
盘点下常见 HDFS JournalNode 异常的问题原因和修复方法
盘点下常见 HDFS JournalNode 异常的问题原因和修复方法 最近在多个客户现场以及公司内部环境,都遇到了因为 JournalNode 异常导致 HDFS 服务不可用的问题,在此总结下相关知识。 1 HDFS HA 高可用和 JournalNode 概述 HDFS namenode 有 SPOF 单点故障…...
![](https://www.ngui.cc/images/no-images.jpg)
深入了解python生成器(generator)
生成器 生成器是 Python 中一种特殊类型的迭代器。生成器允许你定义一个函数来动态产生值,而不是一次性生成所有值并将它们存储在内存中。生成器使用 yield 关键字来逐个返回值。每次调用生成器函数时,函数会在 yield 语句暂停,并记住当前的…...
![](https://img-blog.csdnimg.cn/direct/4762546ba1584a6e8d8a17045692f48f.png)
【Linux】Xshell和Xftp简介_安装_VMware虚拟机使用
1、简介 Xshell简介 Xshell是一款强大的安全终端模拟软件支持SSH1、SSH2以及Microsoft Windows平台的TELNET协议。该软件通过互联网实现到远程主机的安全连接,并通过其创新性的设计和特色帮助用户在复杂的网络环境中高效工作。Xshell可以在Windows界面下访问远端不…...
![](https://www.ngui.cc/images/no-images.jpg)
【轮询负载均衡规则算法设计题】
一、题目描述 给定n台主机(编号1~n)和某批数据包,数据包格式为(抵达主机时刻,负载量)。这里数据每个时刻最多只有1条数据到达。负载量表示该主机处理此数据包总耗时。请计算轮询负载均衡规则下,…...
![](https://img-blog.csdnimg.cn/img_convert/605ee73ff1a500b10d7982e9793b3b0f.jpeg)
张一鸣的产品哲学:与巨头共舞,低调中寻求突破
一、引言 在当今互联网竞争激烈的格局下,与巨头企业打交道是每个新兴科技企业都需面对的挑战。字节跳动创始人张一鸣在多次访谈中分享了他与巨头企业打交道的经验:保持低调、补齐技术、产品和市场各方面的能力。本文将探讨这一策略背后的产品哲学&#…...
![](https://img-blog.csdnimg.cn/direct/cbeed1f0104c4f7380f37a309f3af1cb.png#pic_center)
【面试干货】throw 和 throws 的区别
【面试干货】throw 和 throws 的区别 1、throw1.1 示例 2、throws2.1 示例 3、总结 💖The Begin💖点点关注,收藏不迷路💖 在Java中,throw和throws都与异常处理紧密相关,但它们在使用和含义上有明显的区别。…...
![](https://csdnimg.cn/release/blog_editor_html/release2.3.6/ckeditor/plugins/CsdnLink/icons/icon-default.png?t=N7T8)
安卓手机删除的照片怎么恢复?3个方法,小技巧大作用
你是否曾经不小心删除了手机里的珍贵照片,却不知道怎么恢复?别担心,今天我们就来分享几个简单的小技巧,帮助你轻松找回那些丢失的照片。这些技巧虽然简单,但却能发挥大作用,让你不再为丢失照片而烦恼。手机…...
![](https://img-blog.csdnimg.cn/direct/1366d6d8bb8c4d14a7cb8e5967fa2dd0.png)
Unity制作背包的格子
1.新建一个面板 2.点击面板并添加这个组件 3.点击UI创建一个原始图像,这样我们就会发现图像出现在了面板的左上角。 4.多复制几个并改变 Grid Layout Group的参数就可以实现下面的效果了...
![](https://www.ngui.cc/images/no-images.jpg)
道可云元宇宙每日资讯|厦门:运用元宇宙技术助力直播电商发展
道可云元宇宙每日简报(2024年6月20日)讯,今日元宇宙新鲜事有: 厦门:运用元宇宙技术助力直播电商发展 近日,厦门市商务局印发《厦门市促进直播电商高质量发展若干措施(2024年-2026年࿰…...
![](https://csdnimg.cn/release/blog_editor_html/release2.3.6/ckeditor/plugins/CsdnLink/icons/icon-default.png?t=N7T8)
电脑怎么卸载软件?多个方法合集(2024年新版)
在电脑的日常使用中,我们经常需要安装各种软件来满足不同的需求,但随着时间的推移,可能会出现一些软件不再需要或需要更换的情况。此时,及时从电脑上卸载这些不必要的软件是非常重要的。它不仅可以释放硬盘空间,还可以…...
![](https://img-blog.csdnimg.cn/direct/e8915cc0ae51405fa0e34014d8e34f06.png)
【深度学习基础】详解Pytorch搭建CNN卷积神经网络LeNet-5实现手写数字识别
目录 写在开头 一、CNN的原理 1. 概述 2. 卷积层 内参数(卷积核本身) 外参数(填充和步幅) 输入与输出的尺寸关系 3. 多通道问题 多通道输入 多通道输出 4. 池化层 平均汇聚 最大值汇聚 二、手写数字识别 1. 任务…...
![](https://www.ngui.cc/images/no-images.jpg)
面试技巧:正确回答JavaScript中Map和Object的选择问题
在JavaScript的面试中,对于何时使用Map和Object的选择问题,是一个常见的考察点。这两个数据结构都能存储键值对,但它们各有优势和适用场景。本文将深入探讨两者的区别,并通过实际代码示例来指导您如何选择。 基本概念 Map&#…...
![](https://www.ngui.cc/images/no-images.jpg)
sd StableDiffusion库学习笔记
目录 DeepSpeed realesrgan BasicSR超分辨率,去噪,去模糊,去 JPEG 压缩噪声 segment_anything mmengine controlnet_aux accelerate transfersformer pytorch_fid einops compel transfersformer 文本嵌入调整库 报错:…...
![](https://img-blog.csdnimg.cn/direct/863b0616c5564042b7034e9853bfd2da.png)
【单片机毕业设计选题24017】-基于STM32的禽舍环境监测控制系统(蓝牙版)
系统功能: 系统分为主机端和从机端,主机端主动向从机端发送信息和命令,从机端 收到主机端的信息后回复温湿度氨气浓度和光照强度等信息。 主要功能模块原理图: 电源时钟烧录接口: 单片机和按键输入电路: 主机部分电路: 从机部分电路: 资料获取地址 主…...
![](https://www.ngui.cc/images/no-images.jpg)
每天一个数据分析题(三百七十八)- 系统聚类
在系统聚类方法中,哪种系统聚类是直接利用了组内的离差平方和? A. 最长距离法 B. 重心法 C. Ward法 D. 类平均法 数据分析认证考试介绍:点击进入 题目来源于CDA模拟题库 点击此处获取答案 数据分析专项练习题库 内容涵盖Python&#…...
![](https://img-blog.csdnimg.cn/direct/80d911841e544a80b3a4d769e21bded6.png)
守护系统稳定性的关键技术之看门狗
目录 一、独立看门狗 二、窗口看门狗 三、总结 看门狗定时器(Watchdog Timer,WDT)是嵌入式系统中至关重要的组件,用于监控和维护系统的稳定性。主要是用来监测单片机运行状态和解决程序引起的故障的模块。在由单片机构成的微型…...
![](https://img-blog.csdnimg.cn/direct/b06967cc557e416caad16d84dc588b10.png)
【Linux】进程间通信上 (1.5万字详解)
目录 一.进程间通信介绍 1.1进程间通信的目的 1.2初步认识进程间通信 1.3进程间通信的种类 二.匿名管道 2.1何为管道 2.1实现原理 2.3进一步探寻匿名管道 2.4编码实现匿名管道通信 2.5管道读写特点 2.6基于管道的进程池设计 三.命名管道 3.1实现原理 3.2代码实现 四.…...
![](https://www.ngui.cc/images/no-images.jpg)
测试用例设计:提升测试覆盖率的策略与方法
测试用例设计:提升测试覆盖率的策略与方法 前言测试用例设计的原则提高测试覆盖率的方法测试类型的分析 测试用例设计的基本方法等价类划分边界值分析正交法判定表法因果图法 方法与策略方法策略 如何评价测试用例结论 前言 在软件开发过程中,测试用例设…...
![](https://img-blog.csdnimg.cn/img_convert/e8a1900b548619d3ba9f92ed7f862731.jpeg)
【微服务】什么是Hystrix?一文带你入门Hystrix
文章目录 强烈推荐引言主要功能实现容错应用场景1. 远程服务调用2. 防止级联故障3. 网络延迟和超时管理4. 资源隔离5. 高并发场景6. 熔断与自动恢复7. 故障检测与监控 示例应用场景使用实例1. 引入依赖2. 创建 Hystrix 命令类3. 使用 Hystrix 命令4. 配置 Hystrix5. 实时监控集…...
![](https://www.ngui.cc/images/no-images.jpg)
AI学习指南机器学习篇-支持向量机超参数调优
AI学习指南机器学习篇-支持向量机超参数调优 在机器学习领域中,支持向量机(Support Vector Machines,SVM)是一种非常常用的监督学习模型。它通过寻找一个最优的超平面来进行分类和回归任务。然而,在实际应用中&#x…...
![](https://www.ngui.cc/images/no-images.jpg)
掉电安全文件系统分析
掉电安全FS 掉电安全的文件系统(Power-Fail Safe File Systems)被设计为在电源故障或系统崩溃的情况下仍能保持数据一致性的文件系统。这样的文件系统通常通过使用日志(journaling)或写时复制(copy-on-writeÿ…...
![](https://img-blog.csdnimg.cn/7817e9ae3fe0473e821d74a5de406bf9.png)
南京高端网站建设/网络客服
Yolov5的模型定义分两个阶段,分别是基础模块设计阶段和完整模型搭建阶段。基础模块设计阶段,是基于pytorch架构的基本神经网络算子,进一步构造成具有特定功能和含义的自定义神经网络模块。完整模型搭建阶段,是通过结构化文件&…...
![](/images/no-images.jpg)
一般使用的分辨率的显示密度/seo兼职招聘
单例模式简介 同步锁方式 静态变量方式 单例模式简介 单例模式是一种常见的设计模式,它的核心结构为一个特殊的单例类。通过单例模式可以保证系统中一个类只有一个实例。常见的实现方式有: 懒汉模式:不到万不得已是不会去实例化类&#x…...
![](https://img2018.cnblogs.com/blog/1511102/201905/1511102-20190519195503009-1126604301.png)
微信上如何做网站/鞍山seo优化
1、代码组成部分 app: manifests AndroidManifest.xmljava com.example.honl(androidTest)com.example.honl.muiscoco BaseActivity 抽象类,绑定/解绑ServicebottomInfoFr 点击“更多选项”中的“歌曲信息”弹出的片段cocoPlayerAPP ApplicationConstant 一些常量Lo…...
![](/images/no-images.jpg)
台州网站策划/搜索引擎营销优化诊断训练
一、概念 (1)卷积(CNN) 卷积神经网络(CNN)是深度学习中十分重要的一种算法。卷积是一种数学计算方法,实际上是在卷积核(类似于滤波器,是一个含权重的模板)和输…...
![](https://img-blog.csdnimg.cn/20210105161821886.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaTE5ODYyMQ==,size_16,color_FFFFFF,t_70)
品牌网站建设推荐大蝌蚪/什么是互联网推广
wsdl2java wsimport 的升级版wsdl2java 生成webService java 基本类 apache-cxf 下载 及配置变量 下载地址列表 : http://cxf.apache.org/download.html 下载地址: https://www.apache.org/dyn/closer.lua/cxf/3.4.2/apache-cxf-3.4.2.zip 下载后解压…...
![](/images/no-images.jpg)
淄博网站公司/网络营销渠道可分为哪些
猜字游戏是一款益智游戏,喜欢玩具有挑战性单词游戏的你就可以来体验一下了,给你一个字母板。尝试通过想象相邻字母的单词。您将获得一个分数,根据您使用的字母,你已经使用了多少个字母,并以这些字母相关联的任何修饰符…...