当前位置: 首页 > news >正文

自定义防抖注解

问题场景

在开发中由于可能存在的网络波动问题导致用户重复提交,所以自定义一个防抖注解。设计思路:自定义注解加在接口的方法上,注解中设置了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);}
}

相关文章:

自定义防抖注解

问题场景 在开发中由于可能存在的网络波动问题导致用户重复提交&#xff0c;所以自定义一个防抖注解。设计思路&#xff1a;自定义注解加在接口的方法上&#xff0c;注解中设置了SPEL表达式&#xff0c;可以通过SPEL表达式从接口参数中提取Redis的Key&#xff0c;以这个Key作为…...

【尚庭公寓SpringBoot + Vue 项目实战】登录管理(十八)

【尚庭公寓SpringBoot Vue 项目实战】登录管理&#xff08;十八&#xff09; 文章目录 【尚庭公寓SpringBoot Vue 项目实战】登录管理&#xff08;十八&#xff09;1、登录业务介绍2、接口开发2.1、获取图形验证码2.2、登录接口2.3、获取登录用户个人信息 1、登录业务介绍 登…...

【html】用html+css做地表最强王者荣耀辅助工具

源码&#xff1a; <!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&#xff08;词频-逆文档频率&#xff09;是一种用于衡量文本中词语重要性的方法&#xff0c;特别适用于信息检索和文本挖掘任务。下面会拆分为两部分深入讲解TF-IDF的计算过程&#xff0c;以便更好地理解。 TF-IDF的计算过程可以分为两个主要部分&#xf…...

项目五 OpenStack镜像管理与制作

任务一 理解OpenStack镜像服务 1.1 •什么是镜像 • 镜像通常 是指一系列文件或一个磁盘驱动器的精确副本 。 • 虚拟机 所使用的虚拟磁盘&#xff0c; 实际上是 一种特殊格式的镜像文件 。 • 云 环境下尤其需要 镜像。 • 镜像 就是一个模板&#xff0c;类似于 VMware 的虚拟…...

LabVIEW回热系统热经济性分析及故障诊断

开发了一种利用LabVIEW软件的电厂回热系统热经济性分析和故障诊断系统。该系统针对火电厂回热加热器进行优化&#xff0c;通过实时数据监控与分析&#xff0c;有效提高机组的经济性和安全性&#xff0c;同时降低能耗和维护成本。系统的实施大幅提升了火电厂运行的效率和可靠性&…...

设计模式-迭代器模式

目录 一:基本介绍 二:原理说明 三:案例说明 四:优点 五:缺点 一:基本介绍 1)属于行为模式 2)如果我们的集合元素是用不同的方式实现的,有数组,还有java的集合类,或者还有其他方式,当客户 端要遍历这些集合元素的时候就要使用多种遍历方式,而且还会暴露元素的内部结构,可以…...

UV胶带和UV胶水的应用场景有哪些不同吗?

UV胶带和UV胶水的应用场景有哪些不同吗? UV胶带和UV胶水的应用场景确实存在不同之处&#xff0c;以下是详细的比较和归纳&#xff1a; 一&#xff1a;按使用场景来看&#xff1a; UV胶带的应用场景&#xff1a; 包装行业&#xff1a;UV胶带在包装行业中常用于食品包装、药…...

监控员工上网软件有哪些|4款好用的员工上网行为管理软件推荐

在当今数字化办公环境中&#xff0c;确保网络安全、提升工作效率、以及规范员工上网行为成为企业管理的重要组成部分。 为此&#xff0c;一套高效的员工上网行为管理软件显得尤为关键。 本文将为您推荐五款市场上广受好评的员工上网行为管理软件&#xff0c;帮助您有效监控与管…...

【IPython的使用技巧】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…...

最新AI智能聊天对话问答系统源码(详细图文搭建部署教程)+AI绘画系统(Midjourney),DALL-E3文生图,TTS语音识别输入,文档分析

一、文章前言 随着人工智能技术的持续进步&#xff0c;AI绘画已经发展成为一个日益成熟的领域。越来越多的人开始尝试使用AI绘画软件来创作艺术作品。尽管这些AI绘画软件对绘画领域产生了显著影响&#xff0c;但它们并不会完全取代画师。与传统手绘不同&#xff0c;AI绘画可以…...

项目四 OpenStack身份管理

任务一 理解身份服务 1.1 •Keystone的基本概念 • 认证 &#xff08; Authentication &#xff09; —— 确认 用户身份的过程&#xff0c;又称身份验证 。 • 凭证 &#xff08; Credentials &#xff09; —— 又 称凭据&#xff0c;是用于确认用户身份的数据 。 • 令牌 …...

【后端】websocket学习笔记

文章目录 1. 消息推送常见方式1.1 轮询 VS 长轮询1.2 SSE&#xff08;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 - 吃瓜教程学习笔记(一)

学习视频&#xff1a;第1章-绪论_哔哩哔哩_bilibili 西瓜书对应章节&#xff1a; 第一章 & 第二章 文章目录 机器学习三观What&#xff1a;什么是机器学习&#xff1f;Why: 为什么要学机器学习&#xff1f;1. 机器学习理论研究2. 机器学习系统开发3. 机器学习算法迁移 &…...

Attention Is All You Need论文地址

论文地址 点击即可...

如何优雅的一键下载OpenHarmony活跃分支代码?请关注【itopen: ohos_download】

itopen组织&#xff1a;1、提供OpenHarmony优雅实用的小工具2、手把手适配riscv qemu linux的三方库移植3、未来计划riscv qemu ohos的三方库移植 小程序开发4、一切拥抱开源&#xff0c;拥抱国产化 一、概述 为方便大家每次下载OpenHarmony不同分支/tag代码&#xff0c…...

torch.topk用法

torch.topk用法 介绍使用示例 介绍 官网介绍&#xff1a;https://pytorch.org/docs/stable/generated/torch.topk.html 在指定维度选取k个最大&#xff08;最小&#xff09;的值。 使用示例 values torch.tensor([[2, 1, 3], [1, 2, 3]]) # values # tensor([[2, 1, 3], #…...

终极版本的Typora上传到博客园和csdn

激活插件 下载网址是这个&#xff1a; https://codeload.github.com/obgnail/typora_plugin/zip/refs/tags/1.9.4 解压之后这样的&#xff1a; 解压之后将plugin&#xff0c;复制到自己的安装目录下的resources 点击安装即可&#xff1a; 更改配置文件 "dependencies&q…...

洛谷:P5707【深基2.例12】上学迟到

1. 题目链接 https://www.luogu.com.cn/problem/P5707 【深基2.例12】上学迟到 2. 题目描述 学校和y的家距离s米&#xff0c;s以v的速度去学校&#xff0c;8点之前到&#xff0c;y出门前要打扫10分钟卫生&#xff0c;求s最晚的出门时间 输入&#xff1a;两个正整数路程s&…...

数据治理:数据提取过程中的合规性与安全性

数据治理&#xff1a;数据提取过程中的合规性与安全性 随着数字化时代的到来&#xff0c;数据已经成为企业运营和决策的核心驱动力。然而&#xff0c;在数据提取的过程中&#xff0c;确保数据的合规性和安全性成为了企业面临的重要挑战。数据治理作为一种系统的方法&#xff0…...

Ubuntu系统下交叉编译openssl

一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机&#xff1a;Ubuntu 20.04.6 LTSHost&#xff1a;ARM32位交叉编译器&#xff1a;arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

STM32HAL库USART源代码解析及应用

STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...

pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)

目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 &#xff08;1&#xff09;输入单引号 &#xff08;2&#xff09;万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...

怎么开发一个网络协议模块(C语言框架)之(六) ——通用对象池总结(核心)

+---------------------------+ | operEntryTbl[] | ← 操作对象池 (对象数组) +---------------------------+ | 0 | 1 | 2 | ... | N-1 | +---------------------------+↓ 初始化时全部加入 +------------------------+ +-------------------------+ | …...

PH热榜 | 2025-06-08

1. Thiings 标语&#xff1a;一套超过1900个免费AI生成的3D图标集合 介绍&#xff1a;Thiings是一个不断扩展的免费AI生成3D图标库&#xff0c;目前已有超过1900个图标。你可以按照主题浏览&#xff0c;生成自己的图标&#xff0c;或者下载整个图标集。所有图标都可以在个人或…...

2025-05-08-deepseek本地化部署

title: 2025-05-08-deepseek 本地化部署 tags: 深度学习 程序开发 2025-05-08-deepseek 本地化部署 参考博客 本地部署 DeepSeek&#xff1a;小白也能轻松搞定&#xff01; 如何给本地部署的 DeepSeek 投喂数据&#xff0c;让他更懂你 [实验目的]&#xff1a;理解系统架构与原…...