防抖工具(适用单机部署)
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);}
}