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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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…