Java + Spring Boot 后端防抖切面類AOP代碼問題排查分析

需排查分析的防抖切面類 AOP代碼:

package com.weiyu.aop;import com.weiyu.anno.Debounce;
import com.weiyu.utils.DebounceUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;/*** 防抖切面類 AOP*/
@Component
@Aspect
public class DebounceAspect {@Autowiredprivate DebounceUtil debounceUtil;/*** 切面:防抖*/@Around("@annotation(com.weiyu.anno.Debounce)")public Object debounce(ProceedingJoinPoint joinPoint, Debounce debounceAnnotation) throws Throwable  {System.out.println("=== 防抖切面進入 ===");// 從注解中獲取 keyString key = debounceAnnotation.key();// 從注解中獲取防抖時間long delay = debounceAnnotation.value();// 防抖if (!debounceUtil.checkAndSet(key, delay)) {throw new RuntimeException("操作過于頻繁,請稍后再試");}return joinPoint.proceed();}
}

您提供的代碼存在一個關鍵問題,會導致防抖功能失效。以下是詳細的問題排查和修復方案:

問題診斷

java

復制

下載

@Around("@annotation(com.weiyu.anno.Debounce)")
public Object debounce(ProceedingJoinPoint joinPoint, Debounce debounceAnnotation) throws Throwable {// ...
}

問題出在?切點表達式與參數綁定的不匹配

  1. 切點表達式@annotation(com.weiyu.anno.Debounce)

    • 這種寫法只匹配帶有?@Debounce?注解的方法

    • 不會將注解實例綁定到切面方法的參數

  2. 方法參數Debounce debounceAnnotation

    • 期望接收注解實例

    • 但由于切點表達式沒有指定綁定參數,所以?debounceAnnotation?始終為?null

導致的結果

java

復制

下載

String key = debounceAnnotation.key(); // debounceAnnotation 為 null
long delay = debounceAnnotation.value(); // 拋出 NullPointerException
  1. 當代碼執行到?debounceAnnotation.key()?時

  2. 因為?debounceAnnotation?是?null

  3. 拋出?NullPointerException

  4. 異常可能被全局異常處理器捕獲或日志忽略

  5. 最終表現:防抖功能未生效,沒有看到預期的防抖行為

解決方案

方案1:修改切點表達式(推薦)

java

復制

下載

@Around("@annotation(debounceAnnotation)")
public Object debounce(ProceedingJoinPoint joinPoint, Debounce debounceAnnotation) throws Throwable {// 修復:表達式中的標識符與參數名一致System.out.println("=== 防抖切面進入 ===");// 添加空檢查更安全if (debounceAnnotation == null) {System.err.println("警告:防抖注解未獲取到,直接執行方法");return joinPoint.proceed();}String key = debounceAnnotation.key();long delay = debounceAnnotation.value();// ... 其余邏輯不變
}

為什么有效

  • @annotation(debounceAnnotation)?中的?debounceAnnotation?與參數名匹配

  • AspectJ 會自動將注解實例注入到同名參數

  • 參數?debounceAnnotation?不再為 null

方案2:通過反射獲取注解(替代方案)

java

復制

下載

@Around("@annotation(com.weiyu.anno.Debounce)")
public Object debounce(ProceedingJoinPoint joinPoint) throws Throwable { // 移除參數System.out.println("=== 防抖切面進入 ===");// 通過方法簽名獲取注解MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();Debounce debounceAnnotation = method.getAnnotation(Debounce.class);if (debounceAnnotation == null) {System.err.println("警告:防抖注解未獲取到,直接執行方法");return joinPoint.proceed();}String key = debounceAnnotation.key();long delay = debounceAnnotation.value();// ... 其余邏輯不變
}

完整修復代碼

java

復制

下載

package com.weiyu.aop;import com.weiyu.anno.Debounce;
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.stereotype.Component;import java.lang.reflect.Method;/*** 防抖切面類 AOP*/
@Component
@Aspect
public class DebounceAspect {@Autowiredprivate DebounceUtil debounceUtil;/*** 修復后的防抖切面*/@Around("@annotation(debounceAnnotation)") // 關鍵修改:匹配參數名public Object debounce(ProceedingJoinPoint joinPoint, Debounce debounceAnnotation) throws Throwable {System.out.println("=== 防抖切面進入 ===");// 安全保護:注解為空時直接放行if (debounceAnnotation == null) {System.err.println("?? 防抖注解實例未獲取到,直接執行方法");return joinPoint.proceed();}// 從注解中獲取 keyString key = debounceAnnotation.key();// 從注解中獲取防抖時間long delay = debounceAnnotation.value();// 處理空key情況(重要!)if (key == null || key.isEmpty()) {// 生成默認key:類名+方法名MethodSignature signature = (MethodSignature) joinPoint.getSignature();key = signature.getDeclaringTypeName() + "#" + signature.getName();}System.out.println("防抖參數: key=" + key + ", delay=" + delay + "ms");// 防抖檢查if (!debounceUtil.checkAndSet(key, delay)) {System.out.println("防抖觸發: 拒絕請求");throw new RuntimeException("操作過于頻繁,請稍后再試");}System.out.println("防抖檢查通過,執行方法");return joinPoint.proceed();}
}

關鍵改進點

  1. 切點表達式修正

    • 使用?@annotation(debounceAnnotation)?確保參數綁定

  2. 空注解保護

    • 添加?if (debounceAnnotation == null)?檢查

    • 防止空指針異常導致整個請求失敗

  3. 空key處理

    • 當注解的?key()?為空時自動生成唯一key

    • 使用?類名#方法名?作為默認標識符

    • 避免所有方法共享同一個防抖鎖

  4. 增強日志

    • 添加詳細的執行過程日志

    • 方便調試和問題排查

驗證測試

在 Controller 中添加測試方法:

java

復制

下載

@RestController
public class TestController {@Debounce(key = "testKey", value = 3000)@GetMapping("/test")public String test() {System.out.println("業務方法執行");return "success";}
}

測試步驟:

  1. 快速連續調用兩次?/test?接口

  2. 第一次應該成功

  3. 第二次應該收到 "操作過于頻繁" 錯誤

  4. 觀察控制臺日志輸出

預期日志

text

復制

下載

=== 防抖切面進入 ===
防抖參數: key=testKey, delay=3000ms
防抖檢查通過,執行方法
業務方法執行=== 防抖切面進入 ===
防抖參數: key=testKey, delay=3000ms
防抖觸發: 拒絕請求

為什么日志沒有暴露問題?

在您原始的代碼中,這行日志可能掩蓋了問題:

java

復制

下載

System.out.println("key = " + key + ", delay = " + delay);

當?debounceAnnotation?為 null 時:

java

復制

下載

String key = debounceAnnotation.key(); // 拋出 NullPointerException

這個異常可能是:

  1. 被全局異常處理器捕獲了

  2. 被框架吞掉了

  3. 在日志中被忽略

驗證測試

您可以添加空檢查來驗證:

java

復制

下載

@Around("@annotation(com.weiyu.anno.Debounce)")
public Object debounce(ProceedingJoinPoint joinPoint, Debounce debounceAnnotation) throws Throwable {if (debounceAnnotation == null) {System.err.println("?? 注解參數為null!");return joinPoint.proceed();}// ...
}

結論

您遇到的問題是由于 AspectJ 的參數綁定規則導致的:

  1. @annotation(全限定類名)?只匹配注解類型,不綁定實例

  2. @annotation(參數名)?同時匹配注解類型并將實例綁定到同名參數

  3. 參數名必須完全匹配(包括大小寫)

這個看似微小的語法差異(com.weiyu.anno.Debounce?vs?debounceAnnotation)實際上完全改變了切面的行為方式。這也是為什么 AspectJ 切面開發時需要特別注意參數綁定的原因。

其他注意事項

  1. 全局異常處理

    java

    復制

    下載

    @ControllerAdvice
    public class GlobalExceptionHandler {@ExceptionHandler(RuntimeException.class)public ResponseEntity<String> handleRuntimeException(RuntimeException e) {if ("操作過于頻繁,請稍后再試".equals(e.getMessage())) {return ResponseEntity.status(429).body(e.getMessage()); // HTTP 429 Too Many Requests}return ResponseEntity.status(500).body("服務器錯誤");}
    }
  2. SpEL表達式支持(可選增強):

    java

    復制

    下載

    // 在獲取key后添加
    if (key.contains("#")) {key = parseSpelExpression(joinPoint, key);
    }// SpEL解析方法
    private String parseSpelExpression(ProceedingJoinPoint joinPoint, String expression) {// 實現SpEL解析邏輯
    }
  3. 防抖工具類檢查

    • 確保?DebounceUtil.checkAndSet()?是線程安全的

    • 推薦使用?ConcurrentHashMap?存儲最后一次執行時間

這個修復方案解決了切面參數綁定的核心問題,同時增強了代碼的健壯性和可維護性。

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

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

相關文章

【FreeRTOS-信號量】

參照正點原子以及以下gitee筆記整理本博客&#xff0c;并將實驗結果附在文末。 https://gitee.com/xrbin/FreeRTOS_learning/tree/master 一、信號量簡介 1、什么是信號量 答&#xff1a;信號量是一種解決同步問題的機制&#xff0c;可以實現對共享資源的有序訪問。 假設有…

C++中decltype / auto 類型自動推導的深入講解

一、基本定義 關鍵字含義出現版本auto根據初始化表達式自動推導類型C11decltype根據表達式的類型推導類型C11 二、二者區別 特性autodecltype(expr)用途聲明變量獲取表達式類型是否需要初始化是否&#xff08;可用表達式&#xff0c;如函數參數&#xff09;是否推導引用否&am…

Echarts數據可視化開發教程+120套開源數據可視化大屏H5模板

數據可視化跨越了語言、技術和專業的邊界&#xff0c;是能夠推動實現跨界溝通&#xff0c;實現國際間跨行業的創新的工具。 正如畫家用顏料表達自我&#xff0c;作者用文字講述故事&#xff0c;而統計人員用數字溝通 ...... 同樣&#xff0c;數據可視化的核心還是傳達信息。 …

華為提取版,低調使用!

大家好呀&#xff01;今天想給大家推薦兩款實用軟件&#xff0c;一個是視頻軟件的定制版&#xff0c;另一個是衛星地圖軟件。 01 引言 之前給大家推薦過某秋音樂的定制版&#xff0c;結果被投訴了。以后大家推薦某秋家的軟件要小心&#xff0c;不然很容易違規。 今天推薦的是…

天匯企業的網絡設計與實現

天匯企業網絡的設計與實現 摘要&#xff1a;互聯網技術與通信技術的相互帶動作用&#xff0c;使得兩者皆呈現多樣化的快速發展趨勢&#xff0c;5G的時代序幕在已經逐漸開啟&#xff0c;由此引發的互聯網技術和設備變革必然是各界人士關注的重點&#xff0c;幾乎所有與計算機相…

系統架構設計師:安全架構考點解析與例題

一、安全架構概述 安全架構是系統架構設計中確保信息系統安全性的重要組成部分,它定義了保護系統免受安全威脅的策略、技術和方法。安全架構需要貫穿系統設計的全生命周期,從需求分析到部署運維。 安全架構核心目標 ??保密性??:防止未授權訪問信息??完整性??:防止…

計量經濟學(復習/自用/未完)

補充&#xff1a; 1、多重共線性的補充 所謂的估計標準誤&#xff0c;指的是回歸系數的標準誤差。例如回歸方程&#xff1a; y β0 β1X1 β2X2 e 我們構建的回歸方程的系數的計算得出是基于樣本的。這意味著&#xff0c;我們每從總體中進行一次抽樣&#xff0c;然后計算…

HarmonyOS性能優化——感知流暢優化

在應用開發中&#xff0c;動畫可以為用戶界面增添生動、流暢的交互效果&#xff0c;提升用戶對應用的好感度。然而&#xff0c;濫用動畫也會導致應用性能下降&#xff0c;消耗過多的系統資源&#xff0c;甚至影響用戶體驗。關于感知流暢度請參閱提升動畫感知流暢度。 視覺感知…

基于Python的房屋信息可視化及價格預測系統

開發語言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.10(必須)數據庫&#xff1a;mysql 5.7數據庫工具&#xff1a;Navicat12開發軟件&#xff1a;PyCharm 系統展示 系統首頁 系統登錄 房價預測 房屋管理 房屋分析 個人信息 密碼修改 用戶管理 摘…

(17)-java+ selenium->自動化測試-元素定位大法之By css上

1.簡介 CSS定位方式和xpath定位方式基本相同,只是CSS定位表達式有其自己的格式。CSS定位方式擁有比xpath定位速度快,且比CSS穩定的特性。下面詳細介紹CSS定位方式的使用方法。相對CSS來說,具有語法簡單,定位速度快等優點。 2.CSS定位優勢 CSS定位是平常使用過程中非常重要…

高效I/O處理:模型與多路復用的探討

目錄 一、了解IO模型 &#xff08;一&#xff09;異步IO和同步IO &#xff08;二&#xff09;五種IO快速回顧 二、IO多路復用 &#xff08;一&#xff09;IO 多路復用模型 &#xff08;二&#xff09;select 實現原理 &#xff08;三&#xff09;poll 實現原理 &#x…

行列式展開定理(第三種定義) 線性代數

目錄 1.余子式 2代數余子式 3行列式展開公式&#xff08;常用&#xff09; 本篇的用途是關于三階以上行列式的一般解法。因為對于三階以上行列式我們沒有類似于2階和三階一樣的特殊的求值辦法&#xff0c;而對于我們上一篇講的辦法來說又太復雜了&#xff0c;一般考試幾乎不…

一種輕量級IDS,使用新型特征選擇方法進行早期APT檢測

大家讀完覺得有幫助記得關注和點贊&#xff01;&#xff01;&#xff01; 高級持續性威脅 (APT) 是一種多階段、高度復雜且隱蔽的網絡威脅形式&#xff0c;它通過獲得對網絡的未授權訪問來竊取有價值的數據或破壞目標網絡。這些威脅通常在很長一段時間內未被發現&#xff0c;這…

深入理解 let、var 和 const

JavaScript 中的變量聲明有三種主要方式&#xff1a;var、let 和 const。理解它們之間的差異對于編寫清晰、有效的代碼至關重要。本文將深入探討這三種聲明方式的區別、使用場景以及潛在的陷阱。 一、var 關鍵字 1.1 特點 函數作用域&#xff1a;var 聲明的變量在函數內是局…

RT thread 在gd32f303平臺下rtc bug date獲取時間錯誤始終是1970

現象 時間設置指令 date 2025 6 18 10 28 00 時間獲取指令 date date指定顯示設置OK,但是返回的時間始終是Thu Jan 1 08:00:00 1970 msh >date local time: Thu Jan 1 08:00:00 1970 timestamps: 0 timezone: UTC+

jieba中lcut與cut的區別及用法

jieba 庫中的 cut 和 lcut 是中文分詞的核心函數&#xff0c;兩者的核心區別在于??返回類型??和??適用場景??&#xff0c;具體對比如下&#xff1a; ?? 1. ??核心區別?? ??函數????返回類型????特點????等價操作??jieba.cut生成器&#xff08;G…

LoRA、QLoRA是什么

一&#xff1a; LoRA&#xff08;Low-Rank Adaptation&#xff0c;低秩適應&#xff09;是一種高效的大模型參數微調技術&#xff0c;由Meta在2021年提出。它通過凍結預訓練模型參數&#xff0c;僅訓練少量新增的低秩矩陣&#xff0c;大幅減少了需要訓練的參數量&#xff0c;同…

【web應用】在 Vue 3 中實現餅圖:使用 Chart.js實現餅圖顯示數據分析結果

文章目錄 前言一、準備工作二、實現餅圖組件三、關鍵點解析四、實現效果總結 前言 在現代 Web 應用中&#xff0c;數據可視化是不可或缺的一部分。無論是展示統計信息還是監控關鍵指標&#xff0c;圖表都能幫助用戶更直觀地理解數據。在 Vue 3 項目中&#xff0c;我們可以使用…

分頁數據不準問題分析與解決

大綱 &#x1f4d6; 1、場景 &#x1fab5;2、原因 &#x1f525;3、解決方式&#xff1a;游標分頁 &#x1f4cf;4、一點思考&#x1f4a1;5、全表查詢的優化思路 &#x1f345; 記錄一個分頁不準的問題 1、場景 &#x1fab5; 調用一個第三方List接口&#xff08;帶分頁&am…

MyBatis原理剖析(三)--加載配置文件

下面我們正式進入mybatis的源碼學習&#xff0c;之前我們已經了解過mybatis中通過配置文件來保證與數據庫的交互。配置文件分為核心配置文件和映射配置文件&#xff0c;核心配置文件的主要作用就是加載數據庫的一些配置信息而映射配置文件則是執行對應的sql語句。同時核心配置文…