防抖工具(適用單機部署)
DebounceUtil.java
package com.weiyu.utils;import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.springframework.stereotype.Component;import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;/*** 防抖工具(適用單機部署)*/
@Component
public class DebounceUtil {private final Map<String, Boolean> debounceMap = new ConcurrentHashMap<>();private ScheduledExecutorService scheduler;@PostConstructpublic void init() {scheduler = Executors.newSingleThreadScheduledExecutor();}@PreDestroypublic void destroy() {if (scheduler != null) {scheduler.shutdown();}}/*** 檢查并設置防抖狀態* @param key 唯一標識* @param delay 防抖時間(毫秒)* @return true 允許操作*/public boolean checkAndSet(String key, long delay) {if (debounceMap.containsKey(key)) {// 在冷卻期內return false;}// 增加 keydebounceMap.put(key, true);// 延遲后移除 keyscheduler.schedule(() -> debounceMap.remove(key), delay, TimeUnit.MILLISECONDS);return true;}
}
手動防抖(臨時應用)
Controller 層:WarehouseController.java
......public class WarehouseController {......// 自動注入防抖工具@Autowiredprivate DebounceUtil debounceUtil;// 查詢待入庫的出庫明細數據@GetMapping("/ckd/queryDetailForRk")public Result<List<CkDetail>> queryCkDetailForRk(@RequestParam String ckNo) {log.info("【出庫】,查詢明細數據,/warehouse/ckd/queryDetailForRk,ckNo = {}", ckNo);// 防抖增加的代碼//#####################################################################// 防抖唯一標識:請求路徑 + 用戶名(賬號)String debounceKey = "/warehouse/ckd/queryDetailForRk:" + PublicUtils.getUserName();// 防抖時間:1秒long debounceTime = 1000;if (!debounceUtil.checkAndSet(debounceKey, debounceTime)) {throw new RuntimeException("操作過于頻繁,請稍后再試");}//#####################################################################List<CkDetail> ckDetailList = warehouseService.queryCkDetailForRk(ckNo);return Result.success(ckDetailList);}
}
?注解防抖(項目應用)
1、增加防抖注解 @Debounce
Debounce.java
package com.weiyu.anno;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 防抖注解*/
@Target(ElementType.METHOD) // @Target 是元注解,指定當前注解 Debounce 可以應用的目標元素類型為方法 METHOD
@Retention(RetentionPolicy.RUNTIME) // @Retention 是元注解,指定當前注解 Debounce 的保留策略(生命周期)是 運行時仍可通過反射機制讀取 RUNTIME
public @interface Debounce {// 默認防抖時間long value() default 3000;// 自定義Key(支持SpEL)String key() default "";
}
2、增加防抖切面類 AOP?
DebounceAspect.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;/*** 防抖切面類 AOP*/
@Component
@Aspect
public class DebounceAspect {@Autowiredprivate DebounceUtil debounceUtil;/*** 切面:防抖*/@Around("@annotation(debounceAnnotation)")public Object debounce(ProceedingJoinPoint joinPoint, Debounce debounceAnnotation) throws Throwable {// 安全保護:注解為空時直接放行if (debounceAnnotation == null) {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();}// 防抖if (!debounceUtil.checkAndSet(key, delay)) {throw new RuntimeException("操作過于頻繁,請稍后再試");}return joinPoint.proceed();}
}
Controller 層:WarehouseController.java
......public class WarehouseController {......// 自動注入防抖工具@Autowiredprivate DebounceUtil debounceUtil;// 查詢待入庫的出庫明細數據@GetMapping("/ckd/queryDetailForRk")@Debounce(key = "/warehouse/ckd/queryDetailForRk", value = 1000)public Result<List<CkDetail>> queryCkDetailForRk(@RequestParam String ckNo) {log.info("【出庫】,查詢明細數據,/warehouse/ckd/queryDetailForRk,ckNo = {}", ckNo);// // 防抖增加的代碼
// //#####################################################################
// // 防抖唯一標識:請求路徑 + 用戶名(賬號)
// String debounceKey = "/warehouse/ckd/queryDetailForRk:" + PublicUtils.getUserName();
// // 防抖時間:1秒
// long debounceTime = 1000;
// if (!debounceUtil.checkAndSet(debounceKey, debounceTime)) {
// throw new RuntimeException("操作過于頻繁,請稍后再試");
// }
// //#####################################################################List<CkDetail> ckDetailList = warehouseService.queryCkDetailForRk(ckNo);return Result.success(ckDetailList);}
}