自定义防抖注解
问题场景
在开发中由于可能存在的网络波动问题导致用户重复提交,所以自定义一个防抖注解。设计思路:自定义注解加在接口的方法上,注解中设置了SPEL表达式,可以通过SPEL表达式从接口参数中提取Redis的Key,以这个Key作为判断是否重复提交的依据。如果没有设置SPEL表达式的话就以当前登录用户的ID作为Key。同时在将数据设置到缓存的时候使用Lua脚本执行保证Redis命令的原子性。
代码实现
自定义注解
package com.creatar.common.annotation;import java.lang.annotation.*;/*** 防抖注解** @author: 张定辉* @date: 2024/6/13 上午9:43* @description: 防抖注解*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatLock {/*** SPEL表达式,根据该表达式解析出的值作为key** @return SPEL表达式解析得到的值*/String value();/*** redis前缀*/String prefix() default "repeat_lock::";/*** 错误提示信息*/String message() default "请勿重复提交!";/*** 设置单位时间内禁止重复提交,以秒为单位*/int unitTime() default 3;
}
AOP注解处理器
package com.creatar.common.annotation.handler;import com.creatar.common.annotation.RepeatLock;
import com.creatar.exception.CustomException;
import com.creatar.util.SecurityUtil;
import com.creatar.util.SpelUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.expression.EvaluationContext;
import org.springframework.stereotype.Component;import java.util.Collections;
import java.util.Objects;/*** 防抖注解处理器** @author: 张定辉* @date: 2024/6/13 上午9:49* @description: 防抖注解处理器*/
@Aspect
@RequiredArgsConstructor
@Slf4j
@Component
public class RepeatLockAspect {private final RedisTemplate<String, String> redisTemplate;@Before("@annotation(repeatLock)")public void before(JoinPoint joinPoint, RepeatLock repeatLock) {String redisPrefix = repeatLock.prefix();String errorMessage = repeatLock.message();String value = repeatLock.value();int unitTime = repeatLock.unitTime();MethodSignature signature = (MethodSignature) joinPoint.getSignature();EvaluationContext context = SpelUtil.getContext(joinPoint.getArgs(), signature.getMethod());String key = SecurityUtil.getCurrentUserId();try {key = key + SpelUtil.getValue(context, value, String.class);} catch (Exception e) {log.error("防抖注解获取SPEL表达式失败,类名称:{},方法名称{}\n", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), e);}String redisKey = redisPrefix + key;//如果是重复提交则抛出异常if (isRepeat(redisKey,unitTime)) {throw new CustomException(errorMessage);}}/*** 使用Lua脚本执行原子性的Redis操作,避免由于并发过大从而导致的key永久有效* 如果key不存在则设置value为1并且设置过期时间,** @return 如果没有key则false,如果有key则返回true表示重复提交*/private boolean isRepeat(String key,int unitTime) {String scriptStr = """if redis.call('exists', KEYS[1]) == 0 thenredis.call('set', KEYS[1], 1, 'ex',%s)return falseelsereturn trueend""".formatted(unitTime);RedisScript<Boolean> script = new DefaultRedisScript<>(scriptStr, Boolean.class);Boolean result = redisTemplate.execute(script, Collections.singletonList(key));if (Objects.isNull(result)) {return true;}return result;}
}
应用
package com.creatar.controller;import com.creatar.common.Res;
import com.creatar.common.annotation.RepeatLock;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.security.PermitAll;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;/*** 测试接口** @author: 张定辉* @date: 2024/6/15 下午4:36* @description: 测试接口*/
@RestController
@RequestMapping("/test")
@Tag(name = "测试接口",description = "测试接口")
@PermitAll
public class TestController {@GetMapping("/testLimit")@RepeatLock(value = "#param",unitTime = 5)public Res<String> testLimit(@RequestParam(value = "param")String param) {return Res.success(param);}
}
相关文章:
自定义防抖注解
问题场景 在开发中由于可能存在的网络波动问题导致用户重复提交,所以自定义一个防抖注解。设计思路:自定义注解加在接口的方法上,注解中设置了SPEL表达式,可以通过SPEL表达式从接口参数中提取Redis的Key,以这个Key作为…...
【尚庭公寓SpringBoot + Vue 项目实战】登录管理(十八)
【尚庭公寓SpringBoot Vue 项目实战】登录管理(十八) 文章目录 【尚庭公寓SpringBoot Vue 项目实战】登录管理(十八)1、登录业务介绍2、接口开发2.1、获取图形验证码2.2、登录接口2.3、获取登录用户个人信息 1、登录业务介绍 登…...
【html】用html+css做地表最强王者荣耀辅助工具
源码: <!DOCTYPE html> <html><head><meta charset"utf-8" /><title></title><style>* {margin: 0;padding: 0;}body{background-color: blue;}.con {width: 300px;height: 500px;background-color: rgba(230,…...
TF-IDF、BM25传统算法总结
1. TF-IDF算法 F-IDF(词频-逆文档频率)是一种用于衡量文本中词语重要性的方法,特别适用于信息检索和文本挖掘任务。下面会拆分为两部分深入讲解TF-IDF的计算过程,以便更好地理解。 TF-IDF的计算过程可以分为两个主要部分…...
项目五 OpenStack镜像管理与制作
任务一 理解OpenStack镜像服务 1.1 •什么是镜像 • 镜像通常 是指一系列文件或一个磁盘驱动器的精确副本 。 • 虚拟机 所使用的虚拟磁盘, 实际上是 一种特殊格式的镜像文件 。 • 云 环境下尤其需要 镜像。 • 镜像 就是一个模板,类似于 VMware 的虚拟…...
LabVIEW回热系统热经济性分析及故障诊断
开发了一种利用LabVIEW软件的电厂回热系统热经济性分析和故障诊断系统。该系统针对火电厂回热加热器进行优化,通过实时数据监控与分析,有效提高机组的经济性和安全性,同时降低能耗和维护成本。系统的实施大幅提升了火电厂运行的效率和可靠性&…...
设计模式-迭代器模式
目录 一:基本介绍 二:原理说明 三:案例说明 四:优点 五:缺点 一:基本介绍 1)属于行为模式 2)如果我们的集合元素是用不同的方式实现的,有数组,还有java的集合类,或者还有其他方式,当客户 端要遍历这些集合元素的时候就要使用多种遍历方式,而且还会暴露元素的内部结构,可以…...
UV胶带和UV胶水的应用场景有哪些不同吗?
UV胶带和UV胶水的应用场景有哪些不同吗? UV胶带和UV胶水的应用场景确实存在不同之处,以下是详细的比较和归纳: 一:按使用场景来看: UV胶带的应用场景: 包装行业:UV胶带在包装行业中常用于食品包装、药…...
监控员工上网软件有哪些|4款好用的员工上网行为管理软件推荐
在当今数字化办公环境中,确保网络安全、提升工作效率、以及规范员工上网行为成为企业管理的重要组成部分。 为此,一套高效的员工上网行为管理软件显得尤为关键。 本文将为您推荐五款市场上广受好评的员工上网行为管理软件,帮助您有效监控与管…...
【IPython的使用技巧】
🎥博主:程序员不想YY啊 💫CSDN优质创作者,CSDN实力新星,CSDN博客专家 🤗点赞🎈收藏⭐再看💫养成习惯 ✨希望本文对您有所裨益,如有不足之处,欢迎在评论区提出…...
最新AI智能聊天对话问答系统源码(详细图文搭建部署教程)+AI绘画系统(Midjourney),DALL-E3文生图,TTS语音识别输入,文档分析
一、文章前言 随着人工智能技术的持续进步,AI绘画已经发展成为一个日益成熟的领域。越来越多的人开始尝试使用AI绘画软件来创作艺术作品。尽管这些AI绘画软件对绘画领域产生了显著影响,但它们并不会完全取代画师。与传统手绘不同,AI绘画可以…...
项目四 OpenStack身份管理
任务一 理解身份服务 1.1 •Keystone的基本概念 • 认证 ( Authentication ) —— 确认 用户身份的过程,又称身份验证 。 • 凭证 ( Credentials ) —— 又 称凭据,是用于确认用户身份的数据 。 • 令牌 …...
【后端】websocket学习笔记
文章目录 1. 消息推送常见方式1.1 轮询 VS 长轮询1.2 SSE(server-sent event)服务器发送事件 2. websocket介绍2.1 介绍2.2 原理2.3 websoket API2.3.1 客户端【浏览器】API2.3.2 服务端API 3. 代码实现3.1 流程分析3.2 pom依赖3.3 配置类3.4 消息格式3.5 消息类 4.…...
DataWhale - 吃瓜教程学习笔记(一)
学习视频:第1章-绪论_哔哩哔哩_bilibili 西瓜书对应章节: 第一章 & 第二章 文章目录 机器学习三观What:什么是机器学习?Why: 为什么要学机器学习?1. 机器学习理论研究2. 机器学习系统开发3. 机器学习算法迁移 &…...
Attention Is All You Need论文地址
论文地址 点击即可...
如何优雅的一键下载OpenHarmony活跃分支代码?请关注【itopen: ohos_download】
itopen组织:1、提供OpenHarmony优雅实用的小工具2、手把手适配riscv qemu linux的三方库移植3、未来计划riscv qemu ohos的三方库移植 小程序开发4、一切拥抱开源,拥抱国产化 一、概述 为方便大家每次下载OpenHarmony不同分支/tag代码,…...
torch.topk用法
torch.topk用法 介绍使用示例 介绍 官网介绍:https://pytorch.org/docs/stable/generated/torch.topk.html 在指定维度选取k个最大(最小)的值。 使用示例 values torch.tensor([[2, 1, 3], [1, 2, 3]]) # values # tensor([[2, 1, 3], #…...
终极版本的Typora上传到博客园和csdn
激活插件 下载网址是这个: https://codeload.github.com/obgnail/typora_plugin/zip/refs/tags/1.9.4 解压之后这样的: 解压之后将plugin,复制到自己的安装目录下的resources 点击安装即可: 更改配置文件 "dependencies&q…...
洛谷:P5707【深基2.例12】上学迟到
1. 题目链接 https://www.luogu.com.cn/problem/P5707 【深基2.例12】上学迟到 2. 题目描述 学校和y的家距离s米,s以v的速度去学校,8点之前到,y出门前要打扫10分钟卫生,求s最晚的出门时间 输入:两个正整数路程s&…...
数据治理:数据提取过程中的合规性与安全性
数据治理:数据提取过程中的合规性与安全性 随着数字化时代的到来,数据已经成为企业运营和决策的核心驱动力。然而,在数据提取的过程中,确保数据的合规性和安全性成为了企业面临的重要挑战。数据治理作为一种系统的方法࿰…...
渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...
从物理机到云原生:全面解析计算虚拟化技术的演进与应用
前言:我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM(Java Virtual Machine)让"一次编写,到处运行"成为可能。这个软件层面的虚拟化让我着迷,但直到后来接触VMware和Doc…...
Vue 3 + WebSocket 实战:公司通知实时推送功能详解
📢 Vue 3 WebSocket 实战:公司通知实时推送功能详解 📌 收藏 点赞 关注,项目中要用到推送功能时就不怕找不到了! 实时通知是企业系统中常见的功能,比如:管理员发布通知后,所有用户…...
