Vue3+Spring boot 前后端防抖增強方案

我將基于您提供的防抖切面代碼進行改進,主要優化點包括使用Redis實現分布式防抖、增強鍵生成策略、改進異常處理等。以下是完整的改進方案:

1. 改進的防抖注解 (Debounce.java)

java

復制

下載

package com.weiyu.anno;import java.lang.annotation.*;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Debounce {/*** 防抖時間(毫秒),默認5秒*/long value() default 5000;/*** 自定義鍵值(支持SpEL表達式)*/String key() default "";/*** 防抖鍵類型*/KeyType keyType() default KeyType.METHOD;/*** 錯誤提示信息*/String message() default "操作過于頻繁,請稍后再試";enum KeyType {METHOD,     // 方法級別防抖(默認)USER,       // 用戶級別防抖(需要用戶上下文)IP,         // IP級別防抖PARAM,      // 參數級別防抖(基于參數值)CUSTOM      // 完全自定義鍵}
}

2. 改進的防抖工具類 (DebounceUtil.java)

java

復制

下載

package com.weiyu.utils;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;import java.util.concurrent.TimeUnit;@Component
public class DebounceUtil {@Autowiredprivate StringRedisTemplate redisTemplate;private static final ExpressionParser parser = new SpelExpressionParser();/*** 檢查并設置防抖鎖* @param key 防抖鍵* @param debounceTime 防抖時間(毫秒)* @return 是否允許操作(true:允許,false:防抖中)*/public boolean checkAndSet(String key, long debounceTime) {// 使用SETNX+EXPIRE原子操作Boolean result = redisTemplate.opsForValue().setIfAbsent(key, "1", debounceTime, TimeUnit.MILLISECONDS);return result != null && result;}/*** 生成防抖鍵* @param joinPoint 切點* @param keyType 鍵類型* @param customKey 自定義鍵表達式* @return 生成的防抖鍵*/public String generateKey(ProceedingJoinPoint joinPoint, Debounce.KeyType keyType, String customKey) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();String methodName = signature.getDeclaringTypeName() + "#" + signature.getName();// 1. 處理自定義SpEL表達式if (keyType == Debounce.KeyType.CUSTOM && StringUtils.hasText(customKey)) {return evaluateSpEL(joinPoint, customKey);}// 2. 處理其他鍵類型switch (keyType) {case USER:return "DEBOUNCE:USER:" + getCurrentUserId() + ":" + methodName;case IP:return "DEBOUNCE:IP:" + getClientIp() + ":" + methodName;case PARAM:return "DEBOUNCE:PARAM:" + methodName + ":" + Arrays.hashCode(joinPoint.getArgs());case CUSTOM: // 已處理,此處為fallbackcase METHOD:default:return "DEBOUNCE:METHOD:" + methodName;}}/*** 解析SpEL表達式*/private String evaluateSpEL(ProceedingJoinPoint joinPoint, String expression) {try {StandardEvaluationContext context = new StandardEvaluationContext();// 設置方法參數context.setVariable("args", joinPoint.getArgs());// 設置方法簽名context.setVariable("method", joinPoint.getSignature());// 設置目標對象context.setVariable("target", joinPoint.getTarget());Expression exp = parser.parseExpression(expression);return "DEBOUNCE:CUSTOM:" + exp.getValue(context, String.class);} catch (Exception e) {throw new IllegalArgumentException("Invalid SpEL expression: " + expression, e);}}/*** 獲取當前用戶ID(需要根據實際認證系統實現)*/private String getCurrentUserId() {// 示例:從Spring Security上下文中獲取Authentication authentication = SecurityContextHolder.getContext().getAuthentication();if (authentication != null && authentication.isAuthenticated()) {return authentication.getName();}return "ANONYMOUS";}/*** 獲取客戶端IP*/private String getClientIp() {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();String ip = request.getHeader("X-Forwarded-For");if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");}if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");}if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}return ip;}
}

3. 改進的防抖切面 (DebounceAspect.java)

java

復制

下載

package com.weiyu.aop;import com.weiyu.anno.Debounce;
import com.weiyu.exception.DebounceException;
import com.weiyu.utils.DebounceUtil;
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.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;import java.lang.reflect.Method;/*** 改進的防抖切面(支持Redis分布式鎖)*/
@Aspect
@Component
public class DebounceAspect {@Autowiredprivate DebounceUtil debounceUtil;@Around("@annotation(debounceAnnotation)")public Object debounce(ProceedingJoinPoint joinPoint, Debounce debounceAnnotation) throws Throwable {// 獲取方法上的注解(處理繼承情況)if (debounceAnnotation == null) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();debounceAnnotation = AnnotationUtils.findAnnotation(method, Debounce.class);if (debounceAnnotation == null) {return joinPoint.proceed();}}// 生成防抖鍵String key = debounceUtil.generateKey(joinPoint,debounceAnnotation.keyType(),debounceAnnotation.key());// 獲取防抖時間long debounceTime = debounceAnnotation.value();// 檢查并設置防抖鎖if (!debounceUtil.checkAndSet(key, debounceTime)) {throw new DebounceException(debounceAnnotation.message());}// 執行目標方法try {return joinPoint.proceed();} catch (Throwable throwable) {// 異常時移除防抖鎖(可選)// debounceUtil.removeLock(key);throw throwable;}}
}

4. 自定義防抖異常 (DebounceException.java)

java

復制

下載

package com.weiyu.exception;/*** 防抖業務異常*/
public class DebounceException extends RuntimeException {private int code = 429; // 使用429狀態碼表示操作過于頻繁public DebounceException(String message) {super(message);}public int getCode() {return code;}
}

5. 全局異常處理器 (GlobalExceptionHandler.java)

java

復制

下載

package com.weiyu.exception;import com.weiyu.common.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvice
public class GlobalExceptionHandler {/*** 處理防抖異常*/@ExceptionHandler(DebounceException.class)public Result handleDebounceException(DebounceException e) {return Result.error(e.getCode(), e.getMessage());}
}

6. 統一響應對象 (Result.java)

java

復制

下載

package com.weiyu.common;public class Result<T> {private int code;private String msg;private T data;// 成功靜態方法public static <T> Result<T> success(T data) {Result<T> result = new Result<>();result.setCode(200);result.setMsg("操作成功");result.setData(data);return result;}// 錯誤靜態方法public static <T> Result<T> error(int code, String msg) {Result<T> result = new Result<>();result.setCode(code);result.setMsg(msg);return result;}// Getters and Setters
}

7. 使用示例

java

復制

下載

@RestController
@RequestMapping("/api/download")
public class DownloadController {// 基本方法級別防抖(5秒內只能調用一次)@Debounce@GetMapping("/basic")public Result<String> basicDownload() {// ... 下載邏輯return Result.success("下載成功");}// 用戶級別防抖(每個用戶5秒內只能下載一次)@Debounce(keyType = Debounce.KeyType.USER, value = 5000)@GetMapping("/user")public Result<String> userSpecificDownload() {// ... 下載邏輯return Result.success("下載成功");}// 自定義鍵防抖(基于文件ID)@Debounce(keyType = Debounce.KeyType.CUSTOM,key = "'FILE:' + #fileId", // SpEL表達式message = "該文件正在下載中,請稍后再試")@GetMapping("/file/{fileId}")public Result<String> fileDownload(@PathVariable String fileId) {// ... 下載邏輯return Result.success("下載成功");}// IP級別防抖(每個IP 10秒內只能下載一次)@Debounce(keyType = Debounce.KeyType.IP, value = 10000)@GetMapping("/ip-based")public Result<String> ipBasedDownload() {// ... 下載邏輯return Result.success("下載成功");}
}

8. 前端適配方案

javascript

復制

下載

// 在響應攔截器中處理防抖異常
instance.interceptors.response.use(response => {// ... 正常處理},error => {if (error.response) {// 識別防抖異常(HTTP 429狀態碼)if (error.response.status === 429) {ElMessage.error(error.response.data?.msg || "操作過于頻繁,請稍后再試");return Promise.reject(error);}// 識別業務層防抖異常(code=429)const res = error.response.data;if (res && res.code === 429) {ElMessage.error(res.msg || "操作過于頻繁,請稍后再試");return Promise.reject(error);}}// ... 其他錯誤處理}
);

改進亮點

  1. 分布式支持

    • 使用Redis實現分布式防抖鎖

    • 原子操作(SETNX + EXPIRE)確保并發安全

  2. 靈活的鍵策略

    • 支持方法、用戶、IP、參數、自定義五種鍵類型

    • 集成SpEL表達式引擎實現動態鍵生成

  3. 增強的異常處理

    • 自定義防抖異常類型(DebounceException)

    • 統一使用429狀態碼(Too Many Requests)

    • 可自定義錯誤消息

  4. 注解增強

    • 支持方法繼承的注解查找

    • 可配置防抖時間和錯誤消息

    • 支持SpEL表達式自定義鍵

  5. 集成Spring生態

    • 自動獲取請求上下文(IP、用戶信息)

    • 通過全局異常處理器統一處理

  6. 更精確的防抖

    • 精確到毫秒級別的防抖控制

    • 避免本地內存防抖的分布式問題

這個改進方案保持了原有代碼的結構和風格,同時增加了企業級應用所需的分布式支持和靈活性,特別適合在微服務架構中使用。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/86068.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/86068.shtml
英文地址,請注明出處:http://en.pswp.cn/web/86068.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Cppcheck 使用教程:本地 + CMake + GitHub Actions 自動分析實戰

Cppcheck 是一款強大的 C/C 靜態分析工具&#xff0c;專注于發現內存泄漏、未初始化變量、死代碼、未使用函數等問題。它不依賴編譯器&#xff0c;適合在持續集成和本地開發中快速定位潛在 Bug。 本文將手把手教你如何&#xff1a; 安裝 Cppcheck 在命令行中使用 集成到 CMak…

Mac Parallels Desktop Kali 2025 代理設置

Mac Parallels Desktop Kali 2025 代理設置 核心步驟&#xff1a; kali設置橋接wifi 查看kali和主機ip 運行命令ifconfig查看kali ip&#xff1a; mac主機ip&#xff1a; kali設置proxy ip填寫主機ip&#xff0c;port為主機proxy端口 enjoy

通義靈碼編程智能體深度評測(Qwen3模型+終端操作+MCP工具調用實戰)

1 引言 隨著AI編程助手進入工具鏈集成時代&#xff0c;通義靈碼作為阿里云推出的智能編程解決方案&#xff0c;其Qwen3模型與MCP(Multi-tool Calling Platform)的協同能力引發開發者關注。本文將基于真實開發場景&#xff0c;從代碼理解、終端操作和工具鏈調用三個維度展開深度…

SpringBoot電腦商城項目--商品詳情+加入購物車

商品詳情 1. 持久層 1.1. 規劃sql語句 根據id查詢商品詳情 1.2 mapper層編寫抽象方法 /*** 根據商品id查詢商品詳情* param id 商品id* return 匹配的id商品詳情&#xff0c;如果沒有匹配的數據&#xff0c;則返回null*/Product findById(Integer id); 1.3 xml文件中編寫sq…

上交卡爾動力聯合提出FastDrive!結構化標簽實現自動駕駛端到端大模型更快更強

最近將類人的推理能力融入到端到端自動駕駛系統中已經成為了一個前沿的研究領域。其中&#xff0c;基于視覺語言模型的方法已經吸引了來自工業界和學術界的廣泛關注。 現有的VLM訓練范式嚴重依賴帶有自由格式的文本標注數據集&#xff0c;如圖1(a)所示。雖然這些描述能夠捕捉豐…

C# 委托(什么是委托)

什么是委托 可以認為委托是持有一個或多個方法的對象。當然&#xff0c;一般情況下你不會想要“執行”一個對 象&#xff0c;但委托與典型的對象不同。可以執行委托&#xff0c;這時委托會執行它所“持有"的方法。 本章將揭示創建和使用委托的語法和語義。在本章后面&am…

iTwin briefcase, checkpoint ,standalone

在 iTwin.js 中&#xff0c;briefcase 和 checkpoint 都是 IModel 的不同連接類型&#xff0c;但它們的用途和特性不同&#xff1a; Briefcase 用途&#xff1a;用于本地編輯和同步。通常是用戶從 iModelHub 檢出&#xff08;Check-out&#xff09;后在本地生成的可寫副本。特…

媒體AI關鍵技術研究

一、引言 隨著人工智能技術的迅猛發展&#xff0c;媒體行業正經歷前所未有的變革。AI技術不僅重塑了內容生產和傳播模式&#xff0c;更為媒體創意發展提供了全新可能。在數字化、移動化和信息爆炸的大背景下&#xff0c;傳統媒體面臨巨大挑戰&#xff0c;而AI技術為行業帶來了…

Cargo 與 Rust 項目

一、Rust 項目&#xff1a;現代化的系統編程單元 Rust 項目 是用 Rust 語言編寫的軟件工程單元&#xff0c;具有以下核心特征&#xff1a; 核心組件&#xff1a; src/ 目錄&#xff1a;存放 Rust 源代碼&#xff08;.rs 文件&#xff09; Cargo.toml&#xff1a;項目清單文件…

uni-app總結6-配合iOS App項目開發apple watch app

假設你已經用uni-app開發好了一個iOS端的app,現在想要開發一個配套的apple watch app。改怎么去開發呢?是不是一頭霧水,這篇文章就會介紹一些apple watch app開發的知識以及如何在uni-app開發的iOS app基礎上去開發配套的watch app。 一、apple watch 開發知識 apple watc…

神經網絡的本質 邏輯回歸 python的動態展示

神經網絡的本質 邏輯回歸 python的動態展示 邏輯回歸運行圖相關代碼什么是邏輯回歸和ai的關系邏輯回歸公式流程與實際案例解析**一、邏輯回歸的數學公式流程**1. **線性組合階段**2. **激活函數&#xff08;Sigmoid&#xff09;**3. **概率預測與決策**4. **交叉熵損失函數**5.…

sql server中的with 鎖各種區別

&#x1f4d8; SQL Server 常用 WITH (Hint) 用法與組合場景對照表 Hint 組合作用說明常見用途是否阻塞他人是否讀臟數據備注WITH (NOLOCK)不加共享鎖&#xff0c;允許讀取未提交數據報表導出、大數據分頁??等價于 READ UNCOMMITTED&#xff0c;臟讀風險高WITH (HOLDLOCK)保持…

KES數據庫部署工具使用

一、啟動部署工具 Windows系統 #命令行 ${安裝目錄}/ClientTools/guitools/DeployTools/deploy.exeLinux系統 #命令行 [rootnode ~]# ${安裝目錄}/ClientTools/guitools/DeployTools/deploy二、環境配置 1.硬件要求 #都是最小配置 CPU&#xff1a;主流32或64位 內存&#…

TB62211FNG是一款采用時鐘輸入控制的PWM斬波器的兩相雙極步進電機驅動器

TB62211FNG是一款采用時鐘輸入控制的PWM斬波器的兩相雙極步進電機驅動器。該器件采用BiCD工藝制造&#xff0c;額定電壓為40伏/1.0安培。片上電壓調節器允許使用單一VM電源控制步進電機。 特點&#xff1a; ? 雙極性步進電機驅動器 ? 脈沖寬度調制&#xff08;PWM&#xf…

uni-app項目實戰筆記24--uniapp實現圖片保存到手機相冊

前提條件&#xff1a;微信小程序要想實現保存圖片到本地相冊需要到微信公眾平臺--小程序--開發管理中配置服務器域名中的downloadFile合法域名&#xff1a; \uniapp提供了saveImageToPhotosAlbum API實現保存的圖片到本地相冊。下面是它的配置參數&#xff1a; 參數名類型必填…

面試題-定義一個函數入參數是any類型,返回值是string類型,如何寫出這個函數,代碼示例

在 TypeScript 里&#xff0c;要定義一個入參為any類型、返回值為string類型的函數&#xff0c;可參考下面幾種實現方式&#xff1a; 1. 基礎實現 直接把入參轉換為字符串返回。 function anyToString(input: any): string {return String(input); // 使用String()進行類型轉…

TensorFlow深度學習實戰——Transformer模型評價指標

TensorFlow深度學習實戰——Transformer模型評價指標 0. 前言1. 質量1.1 GLUE1.2 SuperGLUE1.3 SQuAD1.4 RACE1.5 NLP-progress2. 參數規模3. 服務成本相關鏈接0. 前言 可以使用多種類型的指標評估 Transformer 模型。在本節中,我們將學習一些用于評估 Transformer 的關鍵因素…

linux內核學習(一)---內核社區介紹及補丁提交

目錄 一、引言 二、內核源碼 三、內核社區 ------>3.1、社區的組織架構 ------>3.2、內核社區的工作方式 ------>3.3、內核社區核心網站 ------------>3.3.1、Linux Kernel 官網 ------------>3.3.2、Linux Kernel 郵件列表(LKML) ------------>3.3…

輕量級web開發框架之Flask web開發框架學習:get請求數據的發送

Flask是一個使用 Python 編寫的輕量級 Web 應用框架&#xff0c;簡介靈活&#xff0c;可快速構建開發框架。 協作流程示例 客戶端請求 → Web服務器&#xff08;Nginx&#xff09; → WSGI服務器&#xff08;Gunicorn/uWSGI&#xff09;↓WSGI協議傳遞請求數據&#xff08;env…

Vue 3 異步三劍客:Suspense、async setup() 和 await 的戲劇性關系,白屏的解決

文章目錄 &#x1f3ad; Vue 3 異步三劍客&#xff1a;Suspense、async setup() 和 await 的戲劇性關系&#xff0c;白屏的解決&#x1f3ac; 角色介紹&#x1f3ad; 正常演出流程&#xff08;有 Suspense 時&#xff09;&#x1f4a5; 災難場景&#xff08;缺少 Suspense 時&a…