文章目录

  • 自定义脱敏注解
    • 脱敏注解
    • 接口脱敏注解
  • 反射+AOP实现字段脱敏
    • 切面定义
    • 脱敏策略
      • 脱敏策略的接口
      • 电话号码脱敏策略
      • 邮箱脱敏
      • 不脱敏
      • 姓名脱敏
      • 身份证号脱敏
  • Jackson+AOP实现脱敏
    • 定义序列化
    • 序列化实现脱敏
    • 切面定义
  • Jackson+ThreadLocal+拦截器实现脱敏
    • 定义ThreadLocal
    • 自定义序列化
    • 序列化配置
    • 拦截器定义
    • 拦截器添加到spring
  • 脱敏指定接口
  • 总结

主要通过注解+aop+序列化/jackson的方式实现数据脱敏。
实现了接口级别,类级别,避免被全局脱敏等问题。

自定义脱敏注解

脱敏注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.FIELD)  // 作用于字段
@Retention(RetentionPolicy.RUNTIME)  // 运行时保留
public @interface Desensitize {/*** 指定脱敏策略类型*/DesensitizeType type();enum DesensitizeType {PHONE,ID_CARD,EMAIL,NAME,BANK_CARD,ADDRESS;}
}

接口脱敏注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 标记该注解的方法将对其返回值进行脱敏处理*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EnableDesensitize {
}

反射+AOP实现字段脱敏

切面定义

import com.wzw.anno.Desensitize;
import com.wzw.strategy.DesensitizationStrategy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;import java.lang.reflect.Field;/*** 脱敏切面,对返回对象中的字段进行脱敏处理*/
@Aspect
@Component
public class DesensitizeAspect {/*** * @param joinPoint AOP 拦截到的方法,切点* @return* @throws Throwable*/@Around("execution(* com.wzw.controller.UserController.*(..))")public Object desensitizeResponse(ProceedingJoinPoint joinPoint) throws Throwable {// 执行方法得到返回值Object result = joinPoint.proceed();// 如果返回值是简单类型或字符串,直接返回if (result == null || isPrimitiveOrString(result.getClass())) {return result;}// 如果返回值是集合类型,遍历每个元素进行脱敏if (result instanceof Iterable<?>) {((Iterable<?>) result).forEach(this::processDesensitization);return result;}// 如果返回值是单个对象,对象脱敏processDesensitization(result);return result;}/*** 判断是否为基础数据类型或字符串*/private boolean isPrimitiveOrString(Class<?> clazz) {return clazz.isPrimitive() || Number.class.isAssignableFrom(clazz)|| clazz.equals(String.class) || clazz.equals(Boolean.class);}/*** 处理单个对象的脱敏逻辑*/private void processDesensitization(Object obj) {//获取所有字段Field[] fields = obj.getClass().getDeclaredFields();for (Field field : fields) {    //遍历字段field.setAccessible(true);  //忽略安全try {// 查找带有 @Desensitize 注解的字段if (field.isAnnotationPresent(Desensitize.class)) {//获取@Desensitize注解Desensitize annotation = field.getAnnotation(Desensitize.class);//找到注解指定的脱敏策略DesensitizationStrategy strategy = annotation.strategy().getDeclaredConstructor().newInstance();//获取值String originalValue = (String) field.get(obj);//通过脱敏策略脱敏String maskedValue = strategy.desensitize(originalValue);//忽略安全field.setAccessible(true);//设置值field.set(obj, maskedValue);}} catch (Exception e) {e.printStackTrace();}}}
}

脱敏策略

脱敏策略的接口

/*** 脱敏策略接口,所有脱敏算法需实现此接口*/
public interface DesensitizationStrategy {/*** 脱敏方法** @param value 待脱敏值* @return 脱敏后的值*/String desensitize(String value);
}

电话号码脱敏策略

/*** 手机号脱敏*/
public class MobileDesensitizationStrategy implements DesensitizationStrategy{/*** 手机号脱敏* @param value 待脱敏值* @return*/@Overridepublic String desensitize(String value) {if (value == null || value.length() < 11) {return value;}/*** 匹配规则:*      使用正则表达式匹配11位手机号,分成前3位、中间4位、后4位;*      将匹配到的手机号替换为前3位 + **** + 后4位。*/return value.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");}}

邮箱脱敏

/*** 邮箱脱敏*/
public class EmailDesensitizationStrategy implements DesensitizationStrategy{/*** 邮箱脱敏* @param value 待脱敏值* @return*/@Overridepublic String desensitize(String value) {//不包含@,不是邮箱,直接返回if (value == null || !value.contains("@")) {return value;}String[] parts = value.split("@");String username = parts[0];String domain = parts[1];int length = username.length();// 如果用户名长度小于等于2,则替换为一个星号*if (length <= 2) {return "*" + "@" + domain;}// 否则保留首尾字符,中间用星号*代替其余部分。return username.charAt(0) + "*".repeat(length - 2) + username.charAt(length - 1) + "@" + domain;}
}

不脱敏

/*** 默认无脱敏策略*/
public class NoneDesensitizationStrategy implements DesensitizationStrategy {/*** 不脱敏,直接返回* @param value 待脱敏值* @return*/@Overridepublic String desensitize(String value) {return value;}
}

姓名脱敏

import com.wzw.strategy.DesensitizationStrategy;/*** 姓名脱敏实现* 规则:* - 如果姓名为2个字,只显示第一个字 + ** - 如果姓名为3个字,显示第一个字 + * + 最后一个字* - 如果姓名大于3个字,显示第一个字 + ** + 最后一个字*/
public class NameDesensitizationStrategy implements DesensitizationStrategy {@Overridepublic String desensitize(String value) {if (value == null || value.isEmpty()) {return value;}int length = value.length();if (length == 1) {return "*";} else if (length == 2) {return value.charAt(0) + "*";} else if (length == 3) {return value.charAt(0) + "*" + value.charAt(2);} else {return value.charAt(0) + "**" + value.charAt(length - 1);}}
}

身份证号脱敏

import com.wzw.strategy.DesensitizationStrategy;/*** 身份证脱敏实现* 规则:显示前6位和后4位,中间用*号替代(长度保持一致)*/
public class IdCardDesensitizationStrategy implements DesensitizationStrategy {@Overridepublic String desensitize(String value) {if (value == null || value.length() < 10) {// 身份证号码不合法时,原样返回return value;}int length = value.length();int prefixLen = 6;int suffixLen = 4;String prefix = value.substring(0, prefixLen);String suffix = value.substring(length - suffixLen);return prefix + "*".repeat(length - prefixLen - suffixLen) + suffix;}
}

Jackson+AOP实现脱敏

定义序列化

定义两个,是避免序列化冲突,只有手动调用的时候,才使用自定义的序列化脱敏

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.wzw.serializer.DesensitizeSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;@Configuration
public class DesensitizeConfig {//默认的序列化实现@Primary@Beanpublic ObjectMapper defaultObjectMapper() {return new ObjectMapper();}//脱敏的序列化实现注入@Bean("desensitizeObjectMapper")public ObjectMapper desensitizeObjectMapper() {ObjectMapper mapper = new ObjectMapper();SimpleModule module = new SimpleModule();// 注册脱敏序列化器module.addSerializer(String.class, new DesensitizeSerializer());mapper.registerModule(module);return mapper;}
}

序列化实现脱敏

package com.wzw.serializer;import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.ser.std.StringSerializer;
import com.wzw.anno.Desensitize;import java.io.IOException;
import java.util.Objects;public class DesensitizeSerializer extends JsonSerializer<String> implements ContextualSerializer {private Desensitize.DesensitizeType desensitizeType;public DesensitizeSerializer() {}private DesensitizeSerializer(Desensitize.DesensitizeType type) {this.desensitizeType = type;}@Overridepublic void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {// 直接执行脱敏逻辑(无需检查开关状态)if (desensitizeType != null && value != null) {String desensitizedValue = desensitizeByType(value, desensitizeType);gen.writeString(desensitizedValue);} else {gen.writeString(value);}}@Overridepublic JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {if (property == null) {return new StringSerializer();}// 仅处理 String 类型字段if (!Objects.equals(property.getType().getRawClass(), String.class)) {return new StringSerializer();}// 获取字段上的 @Desensitize 注解Desensitize desensitize = property.getAnnotation(Desensitize.class);if (desensitize != null) {return new DesensitizeSerializer(desensitize.type());}// 无注解字段使用默认序列化器return new StringSerializer();}// 脱敏逻辑private String desensitizeByType(String value, Desensitize.DesensitizeType type) {if (value == null || value.isEmpty()) {return value;}switch (type) {case PHONE:// 只有11位手机号才脱敏if (value.matches("\\d{11}")) {return value.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");}return value;case ID_CARD:// 只有18位身份证号才脱敏if (value.matches("\\d{18}")) {return value.replaceAll("(\\d{6})\\d{8}(\\d{4})", "$1********$2");}return value;case EMAIL:// 简单匹配邮箱格式if (value.matches("[^@]+@[^@]+\\.[^@]+")) {return value.replaceAll("(\\w)[^@]*@", "$1****@");}return value;default:return value;}}
}

切面定义

import com.fasterxml.jackson.databind.ObjectMapper;
import com.wzw.anno.EnableDesensitize;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;@Aspect
@Component
public class DesensitizeAspect {//自定义的序列化private final ObjectMapper desensitizeObjectMapper;//手动指定自定义的序列化public DesensitizeAspect(@Qualifier("desensitizeObjectMapper") ObjectMapper desensitizeObjectMapper) {this.desensitizeObjectMapper = desensitizeObjectMapper;}@Around("@within(com.wzw.anno.EnableDesensitize) || @annotation(com.wzw.anno.EnableDesensitize)")public Object desensitizeResponse(ProceedingJoinPoint joinPoint) throws Throwable {// 获取方法或类上的 EnableDesensitize 注解MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();Class<?> targetClass = joinPoint.getTarget().getClass();// 检查方法或类是否被 EnableDesensitize 注解标记boolean methodAnnotated = method.isAnnotationPresent(EnableDesensitize.class);boolean classAnnotated = targetClass.isAnnotationPresent(EnableDesensitize.class);// 如果方法或类被标注,则执行脱敏逻辑if (methodAnnotated || classAnnotated) {// 执行原方法获取返回值Object result = joinPoint.proceed();// 关键:使用带脱敏序列化器的 ObjectMapper 重新序列化if (result != null) {String json = desensitizeObjectMapper.writeValueAsString(result);return desensitizeObjectMapper.readValue(json, result.getClass());}return result;}// 否则直接返回结果return joinPoint.proceed();}
}

Jackson+ThreadLocal+拦截器实现脱敏

请求时通过拦截器设置 ThreadLocal 标记 → 返回时 Jackson 序列化器读取标记并决定是否脱敏

定义ThreadLocal

public class DesensitizeContextHolder {private static final ThreadLocal<Boolean> DESENSITIZE_ENABLED = new ThreadLocal<>();public static void setDesensitizeEnabled(boolean enabled) {DESENSITIZE_ENABLED.set(enabled);}public static boolean isDesensitizeEnabled() {return Boolean.TRUE.equals(DESENSITIZE_ENABLED.get());}public static void clear() {DESENSITIZE_ENABLED.remove();}
}

自定义序列化

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.ser.std.StringSerializer;
import com.wzw.anno.Desensitize;
import com.wzw.util.DesensitizeContextHolder;import java.io.IOException;public class DesensitizeSerializer extends JsonSerializer<String> implements ContextualSerializer {private Desensitize.DesensitizeType type;public DesensitizeSerializer() {}public DesensitizeSerializer(Desensitize.DesensitizeType type) {this.type = type;}@Overridepublic void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {// 关键逻辑:根据ThreadLocal状态决定是否脱敏if (DesensitizeContextHolder.isDesensitizeEnabled() && value != null && type != null) {gen.writeString(desensitize(value, type));} else {gen.writeString(value);}}@Overridepublic JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {Desensitize annotation = property.getAnnotation(Desensitize.class);if (annotation != null) {return new DesensitizeSerializer(annotation.type());}return new StringSerializer(); // 显式返回默认字符串序列化器}private String desensitize(String value, Desensitize.DesensitizeType type) {// 脱敏逻辑(如手机号中间四位替换为*)switch (type) {case PHONE: return value.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");case ID_CARD: return value.replaceAll("(\\d{6})\\d{8}(\\d{4})", "$1********$2");case EMAIL: return value.replaceAll("(\\w)[^@]*@", "$1****@");default: return value;}}
}

序列化配置

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.wzw.serializer.DesensitizeSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;@Configuration
public class DesensitizeConfig {@Beanpublic ObjectMapper objectMapper() {ObjectMapper mapper = new ObjectMapper();SimpleModule module = new SimpleModule();module.addSerializer(String.class, new DesensitizeSerializer());mapper.registerModule(module);return mapper;}
}

拦截器定义

import com.wzw.anno.EnableDesensitize;
import com.wzw.util.DesensitizeContextHolder;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;@Component
public class DesensitizeInterceptor implements HandlerInterceptor {public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {if (handler instanceof HandlerMethod) {HandlerMethod handlerMethod = (HandlerMethod) handler;// 检查方法或类上是否有@EnableDesensitize注解boolean shouldDesensitize =handlerMethod.hasMethodAnnotation(EnableDesensitize.class) ||handlerMethod.getBeanType().isAnnotationPresent(EnableDesensitize.class);// 设置ThreadLocal标记DesensitizeContextHolder.setDesensitizeEnabled(shouldDesensitize);}return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex) {// 清理ThreadLocal,避免内存泄漏DesensitizeContextHolder.clear();}}

拦截器添加到spring

import com.wzw.handler.DesensitizeInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new DesensitizeInterceptor()).addPathPatterns("/**"); // 拦截所有请求}
}

脱敏指定接口

反射和jackson都是一样的

  • 脱敏指定包下的所有接口
    修改切面的拦截
@Around("execution(* com.wzw.controller..*.*(..))")
  • 脱敏指定controller下的所有接口
    修改切面的拦截
@Around("execution(* com.wzw.controller.UserController.*(..))")
  • 脱敏指定controller或指定接口
    多个controller,有的需要脱敏,有的不需要,再使用切面就不合适了,新增一个注解,用来标注需要脱敏的接口或者controller。
    修改切面的拦截

    @within(…):如果当前类上有 @EnableDesensitize 注解,则拦截所有方法;
    @annotation(…):如果当前方法上有 @EnableDesensitize 注解,则拦截该方法。

    @Around("@within(com.wzw.anno.EnableDesensitize) || @annotation(com.wzw.anno.EnableDesensitize)")
    
  • 测试

    @GetMapping("/list")
    @EnableDesensitize
    public List<User> list() {return userService.list();
    }
    
    @RestController
    @RequestMapping("/user")
    @EnableDesensitize
    public class UserController {
    

总结

实现方式平均响应CPU负载内存占用性能影响因素简单说明总结
✅ Jackson + ThreadLocal + 拦截器🟢 1.2ms无反射、无对象拷贝、ThreadLocal 控制序列化开关最推荐方式,性能最佳,线程安全,不影响业务结构⭐⭐⭐⭐⭐ 强烈推荐
🟡 Jackson + AOP(不含 ThreadLocal)🟡 2.6ms可能需要动态构造 ObjectMapper 或序列化前做标记判断实现简单,无反射,但有状态传递或序列化判断逻辑⭐⭐⭐ 适中,谨慎使用
🔴 反射 + AOP 修改字段值🔴 4.5ms反射操作、多字段遍历、对象深拷贝或原对象修改性能最差,反射慢、内存开销大、易出错,且不可用于不可变对象⭐ 不推荐生产使用

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

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

相关文章

Spring核心原理的快速入门:快速了解IoC与DI

IoC IoC&#xff1a;Inversion of Control(控制反转) Spring是一个包含了众多工具的IoC容器(即bean&#xff1a;spring管理的对象),也就是说Spring 是一个“控制反转”的容器。 之前是对象本身管理自己的生命周期等等&#xff0c;现在交给spring来管理对象的生命周期 IoC介绍 …

ffmpeg 中config 文件一些理解

依赖检查 config中看到最多的是&#xff1a; ... nvenc_deps"ffnvcodec" nvenc_deps_any"libdl LoadLibrary" nvenc_encoder_deps"nvenc" ... h264_crystalhd_decoder_select"crystalhd h264_mp4toannexb_bsf h264_parser" h264_cuvid…

Digital Rainwater Collection System (v1.0)

The law doesn’t punish the masses. If only one guy runs his own rainwater system, he gets fined for “illegal mining.” But if millions of households self-host their “digital wells,” the whole centralized model collapses. Cloud providers and regulators …

NFS文件存储及部署论坛(小白的“升级打怪”成长之路)

目录 一、概述 NFS挂载原理 NFS工作原理 RPC与NFS通讯过程 二、NFS服务安装与启停 NFS服务安装 NFS服务启停 三、NFS服务配置文件 四、NFS文件共享配置文件 配置参数说明 五、命令解析 六、客户端访问 七、客户端挂载 实战案例 部署NFS文件存储及discuz论坛应用 …

JavaScript 对象创建:new 操作符全解析

引言 在 JavaScript 中&#xff0c;new 操作符是实现面向对象编程的​​核心机制​​之一。本文将从原理层面对 new 操作符进行深度剖析&#xff0c;探讨其工作机制、内部实现和实际应用场景。无论您是 JavaScript 初学者还是资深开发者&#xff0c;都能从本文获得以下知识和技…

Spring Boot + Vue.js 全栈开发:从前后端分离到高效部署,打造你的MVP利器!

文章目录一、为何选择 Spring Boot Vue.js&#xff1f;全栈开发的“黄金搭档”&#xff01;二、项目初始化与基础架构搭建2.1 后端&#xff1a;初始化 Spring Boot 项目2.2 前端&#xff1a;初始化 Vue.js 项目2.3 核心配置&#xff1a;打通前后端通信与跨域&#xff01;后端 …

容器技术技术入门与Docker环境部署

目录 一&#xff1a;Docker 概述 1&#xff1a;什么是Docker 2:Docker 的优势 3&#xff1a;Docker的应用场景 4&#xff1a;Docker核心概念 二&#xff1a;Docker 安装 三&#xff1a;Docker 镜像操作 1&#xff1a;获取镜像 2&#xff1a;查看镜像信息 3&#xff1a…

构建高效分布式系统:bRPC组合Channels与HTTP/H2访问指南

构建高效分布式系统&#xff1a;bRPC组合Channels与HTTP/H2访问指南 引言 在现代分布式系统中&#xff0c;下游服务访问的复杂性日益增加。bRPC通过组合Channels和HTTP/H2访问优化&#xff0c;提供了解决多层级RPC调用、负载均衡和协议兼容性问题的完整方案。本文将深入解析两大…

WSL创建Ubuntu子系统与 VS code 开发

文章目录一、打开Windows的虚拟化基础功能二、安装WSL和Ubuntu1. 安装 WSL2. 安装 Ubuntu三、 VScode一、打开Windows的虚拟化基础功能 控制面板-程序和功能-启动或关闭Windows功能&#xff0c;勾选适用于Linux的Windows子系统、虚拟机平台&#xff0c; 完成后根据提示重启电脑…

AlpineLinux二进制文件部署prometheus

在Alpine Linux上通过二进制文件部署Prometheus的步骤如下: 创建用户和组: groupadd prometheus useradd -g prometheus -m -s /sbin/nologin prometheus下载Prometheus二进制文件: 你可以从Prometheus的官方GitHub发布页面下载最新的二进制文件。例如,使用wget命令: wget…

IoT 小程序:如何破解设备互联的碎片化困局?

一、IoT 设备管理为何需要轻量化解决方案&#xff1f;随着物联网设备规模爆发式增长 —— 预计 2025 年全球连接数将达 270 亿台&#xff0c;传统 Native 应用开发模式的弊端日益凸显&#xff1a;某智能家居厂商开发 3 款主流设备 APP&#xff0c;需维护 iOS/Android/ 小程序 3…

Word 怎么让字变大、变粗、换颜色?

这是Word中最常用也最基础的操作之一。学会它&#xff0c;你的文档就会立刻变得重点突出&#xff0c;清晰易读。 记住一个核心前提&#xff1a;无论做什么格式修改&#xff0c;第一步永远是【先选中你要修改的文字】。 你可以把鼠标放在文字的开头&#xff0c;按住左键&#xf…

Ruby 安装 - Linux

Ruby 安装 - Linux 引言 Ruby 是一种广泛使用的高级编程语言,以其简洁、优雅和强大的功能而闻名。在 Linux 系统上安装 Ruby 是许多开发者的首要任务。本文将详细介绍如何在 Linux 系统上安装 Ruby,包括准备工作、安装过程和常见问题解决。 准备工作 在开始安装 Ruby 之前…

数组的应用示例

任意输入【0,9】范围内的整数&#xff0c;统计输入的每一种数字的个数并输出&#xff0c;输入-1结束程序 #include <stdio.h> int main(){const int number 10;int x;int i;int count[number];for ( i 0; i < number; i){count[i] 0;}printf("请输入0&#xf…

鸿蒙智行6月交付新车52747辆 单日交付量3651辆

近日&#xff0c;鸿蒙智行公布最新销量数据&#xff0c;6月单月全系交付52747辆&#xff0c;单日交付量3651辆&#xff0c;分别刷新鸿蒙智行单月、单日销量历史新高。仅用39个月实现全系累计交付80万辆&#xff0c;创下新势力汽车最快交付纪录。 尊界S800自5月30日上市以来&…

基于模糊控制及BP神经网络开关磁阻电机的matlab仿真

1.模型简介本仿真模型基于MATLAB/Simulink&#xff08;版本MATLAB 2015Rb&#xff09;软件。2.仿真算法:1&#xff09;采用转速、转矩双闭环控制算法&#xff1b;2&#xff09;外环是速度环&#xff0c;采用改进复合模糊控制&#xff0c;实现速度跟踪&#xff1b;3&#xff09;…

最新团购源码商城 虚拟商城系统源码 全开源

内容目录一、详细介绍二、效果展示1.部分代码2.效果图展示三、学习资料下载一、详细介绍 最新团购源码商城 虚拟商城系统源码 全开源 基于PHP开发的多功能在线商城系统&#xff0c;适合个人、小型企业或创业团队快速搭建自己的商品销售平台。系统界面美观&#xff0c;功能丰富…

Visual Studio 旧版软件下载教程

一、前言最近在开发过程中编译使用Cuda的版本较低&#xff0c;导致与最新的Visual Studio Community 2022 17.14.8不兼容。编译报错如下&#xff1a;[cmake] C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\yvals_core.h(902): e…

乐橙亮相2025广州建博会:用AI重新定义家庭安全与技术边界

在智能锁迈入AI新时代的关键节点&#xff0c;谁才是真正的技术引领者&#xff1f;2025年广州建博会&#xff0c;乐橙用一场“不炫技、重本质”的深度展演给出了答案。智哪儿在现场了解到&#xff0c;在A区3.1-28展位&#xff0c;乐橙围绕“智启新境 All in Intelligent”这一主…

快速搭建服务器,fetch请求从服务器获取数据

1.strapi首先strapi是一个api管理系统&#xff0c;可以让我们直接用网页的形式去定义自己的api&#xff0c;包括设置模型和权限等功能。首先直接在项目目录里面安装库。npx create-strapilatest server --quickstart这样就可以直接在项目目录创建一个连接数据库的服务器了。不用…