java 接口防抖

server/2024/12/2 7:32:44/

防抖:防止重复提交

在Web系统中,表单提交是一个非常常见的功能,如果不加控制,容易因为用户的误操作或网络延迟导致同一请求被发送多次,进而生成重复的数据记录。要针对用户的误操作,前端通常会实现按钮的loading状态,阻止用户进行多次点击。而对于网络波动造成的请求重发问题,仅靠前端是不行的。为此,后端也应实施相应的防抖逻辑,确保在网络波动的情况下不会接收并处理同一请求多次。

限制 用户在请求返回之前再次进行请求(前台按钮多次点击)

思路:在第一次请求时对该用户加锁,如果锁存在就不执行方法

1. 定一个注解

java">import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME) 
public @interface PreventShaking {/*** redis锁前缀 (用于区分方法)*/String prefix() default "";/***  用来辨别是否是一次重复的请求,支持SpEL,可以从方法的入参中获取*/String key();/*** redis锁过期时间  默认3秒*/int expire() default 3;/*** 提示信息*/String message() default "请求过于频繁,请稍后再试";}

2. AOP 切面

java">import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
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 javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Arrays;@Component
@Aspect
@Slf4j
public class PreventShakingAspect {private static final String INTERFACE_SHAKING = "interface:shaking:";@Resourceprivate RedisTemplate redisTemplate;@Pointcut("@annotation(PreventShaking)")public void pointCut() {log.info("接口防抖");}@Around("pointCut()")public Object interceptor(ProceedingJoinPoint joinPoint) {// 获取执行的方法Signature signature = joinPoint.getSignature();MethodSignature methodSignature = (MethodSignature) signature;Method method = methodSignature.getMethod();PreventShaking shaking = method.getAnnotation(PreventShaking.class);// 创建 SpEL 上下文StandardEvaluationContext context = new StandardEvaluationContext();// 设置方法参数作为变量Object[] args = joinPoint.getArgs();String[] parameterNames = ((MethodSignature) signature).getParameterNames();for (int i = 0; i < args.length; i++) {// 将参数放入上下文context.setVariable(parameterNames[i], args[i]);}// 解析 EL 表达式ExpressionParser parser = new SpelExpressionParser();Expression expression = parser.parseExpression(shaking.key());String key = StringUtils.hasText(shaking.prefix()) ? shaking.prefix() : method.getName();key = INTERFACE_SHAKING +  key + ":" + expression.getValue(context);// 如果key存在缓存中 返回0;不存在就添加并访问1String script = "if redis.call('exists', KEYS[1]) == 1 then\n" +"    \n" +"    return 0\n" +"else\n" +"    redis.call('set', KEYS[1], \"1\", \"ex\", ARGV[1])\n" +"    \n" +"    return 1\n" +"end";DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);Object execute = redisTemplate.execute(redisScript, Arrays.asList(key), shaking.expire());if (Long.valueOf(String.valueOf(execute)) <= 0){log.warn("您的操作太快了,请稍后重试");throw new RuntimeException(shaking.message());}try {return joinPoint.proceed();} catch (Throwable e) {log.error("系统异常", e);throw new RuntimeException("系统异常");}finally {// 方法执行结束,释放锁redisTemplate.delete(key);}}}

参考:

SpringBoot 接口防抖(防重复提交)的一些实现方案_requestlock-CSDN博客

一个注解,优雅的实现接口幂等性!-CSDN博客

java接口防抖的优雅处理_java 接口防抖-CSDN博客


http://www.ppmy.cn/server/146651.html

相关文章

【西瓜书】支持向量机(SVM)

支持向量机&#xff08;Support Vector Machine&#xff0c;简称SVM&#xff09;。 超平面 分类学习最基本的想法就是基于训练集合D在样本空间中找到一个划分超平面&#xff0c;将不同类别的样本分开。 但能将训练样本分开的划分超平面可能有很多&#xff0c;应该努力去找到哪…

当新能源遇见低空经济:无人机在光伏领域的创新应用

随着全球能源结构的转型和技术的不断进步&#xff0c;新能源行业已成为推动经济社会发展的重要力量。其中&#xff0c;低空经济作为新兴的战略性产业&#xff0c;正深刻改变着人类社会的出行方式和产业链格局。在这一背景下&#xff0c;无人机与光伏产业的结合&#xff0c;不仅…

.net core 创建linux服务,并实现服务的自我更新

目录 创建服务创建另一个服务&#xff0c;用于执行更新操作给你的用户配置一些systemctl命令权限 创建服务 /etc/systemd/system下新建服务配置文件&#xff1a;yourapp.service&#xff0c;内容如下&#xff1a; [Unit] Descriptionyourapp Afternetwork.target[Service] Ty…

Spring Boot优雅读取配置信息 @EnableConfigurationProperties

很多时候我们需要将一些常用的配置信息比如oss等相关配置信息放到配置文件中。常用的有以下几种&#xff0c;相信大家比较熟悉&#xff1a; 1、Value(“${property}”) 读取比较简单的配置信息&#xff1a; 2、ConfigurationProperties(prefix “property”)读取配置信息并与 …

嵌入式Linux中常用的文件系统类型

嵌入式Linux系统中使用的文件系统类型多种多样&#xff0c;每种都有其特点和适用场景。以下是几种常见的嵌入式Linux文件系统类型及其特性&#xff1a; 只读压缩文件系统 SquashFS&#xff1a;一种高度压缩的只读文件系统&#xff0c;适合用于固件映像&#xff0c;它能够提供高…

使用Gradle编译前端的项目

使用Gradle编译前端的项目 前言项目结构根项目&#xff08;parent-project&#xff09;的 settings.gradle.kts后端项目&#xff08;backend&#xff09;的 build.gradle.kts前端项目&#xff08;frontend&#xff09;的 build.gradle.kts打包bootJar 前言 最近的项目都是使用…

猴子吃桃问题

题目描述 猴子吃桃问题。猴子第1天摘下若干个桃子&#xff0c;当即吃了一半&#xff0c;还不过瘾&#xff0c;又多吃了一个。第2天早上又将剩下的桃子吃掉一半&#xff0c;又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想再吃时&#xff0c;就只剩一个…

155. TWEEN.JS点按钮,相机飞行靠近观察设备

实际开发的的时候&#xff0c;一个较大的三维场景&#xff0c;有很多不同的设备或物品&#xff0c;你可能希望通过UI按钮点击切换到不同视角&#xff0c;观察某个区域&#xff0c;或者说放大观察某个特定的物品或设备。 按钮 切换相机位置和视角的按钮 <div class"p…