springboot數據脫敏(接口級別)

文章目錄

  • 自定義脫敏注解
    • 脫敏注解
    • 接口脫敏注解
  • 反射+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這樣就可以直接在項目目錄創建一個連接數據庫的服務器了。不用…