自定义防抖注解

2024-06-16 08:28
文章标签 自定义 注解 防抖

本文主要是介绍自定义防抖注解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

问题场景

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

这篇关于自定义防抖注解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1065949

相关文章

在 Spring Boot 中使用 @Autowired和 @Bean注解的示例详解

《在SpringBoot中使用@Autowired和@Bean注解的示例详解》本文通过一个示例演示了如何在SpringBoot中使用@Autowired和@Bean注解进行依赖注入和Bean... 目录在 Spring Boot 中使用 @Autowired 和 @Bean 注解示例背景1. 定义 Stud

Spring排序机制之接口与注解的使用方法

《Spring排序机制之接口与注解的使用方法》本文介绍了Spring中多种排序机制,包括Ordered接口、PriorityOrdered接口、@Order注解和@Priority注解,提供了详细示例... 目录一、Spring 排序的需求场景二、Spring 中的排序机制1、Ordered 接口2、Pri

Idea实现接口的方法上无法添加@Override注解的解决方案

《Idea实现接口的方法上无法添加@Override注解的解决方案》文章介绍了在IDEA中实现接口方法时无法添加@Override注解的问题及其解决方法,主要步骤包括更改项目结构中的Languagel... 目录Idea实现接China编程口的方法上无法添加@javascriptOverride注解错误原因解决方

Java中基于注解的代码生成工具MapStruct映射使用详解

《Java中基于注解的代码生成工具MapStruct映射使用详解》MapStruct作为一个基于注解的代码生成工具,为我们提供了一种更加优雅、高效的解决方案,本文主要为大家介绍了它的具体使用,感兴趣... 目录介绍优缺点优点缺点核心注解及详细使用语法说明@Mapper@Mapping@Mappings@Co

CSS自定义浏览器滚动条样式完整代码

《CSS自定义浏览器滚动条样式完整代码》:本文主要介绍了如何使用CSS自定义浏览器滚动条的样式,包括隐藏滚动条的角落、设置滚动条的基本样式、轨道样式和滑块样式,并提供了完整的CSS代码示例,通过这些技巧,你可以为你的网站添加个性化的滚动条样式,从而提升用户体验,详细内容请阅读本文,希望能对你有所帮助...

Java中注解与元数据示例详解

《Java中注解与元数据示例详解》Java注解和元数据是编程中重要的概念,用于描述程序元素的属性和用途,:本文主要介绍Java中注解与元数据的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参... 目录一、引言二、元数据的概念2.1 定义2.2 作用三、Java 注解的基础3.1 注解的定义3.2 内

SpringBoot使用注解集成Redis缓存的示例代码

《SpringBoot使用注解集成Redis缓存的示例代码》:本文主要介绍在SpringBoot中使用注解集成Redis缓存的步骤,包括添加依赖、创建相关配置类、需要缓存数据的类(Tes... 目录一、创建 Caching 配置类二、创建需要缓存数据的类三、测试方法Spring Boot 熟悉后,集成一个外

SpringBoot 自定义消息转换器使用详解

《SpringBoot自定义消息转换器使用详解》本文详细介绍了SpringBoot消息转换器的知识,并通过案例操作演示了如何进行自定义消息转换器的定制开发和使用,感兴趣的朋友一起看看吧... 目录一、前言二、SpringBoot 内容协商介绍2.1 什么是内容协商2.2 内容协商机制深入理解2.2.1 内容

使用@Slf4j注解,log.info()无法使用问题

《使用@Slf4j注解,log.info()无法使用问题》在使用Lombok的@Slf4j注解打印日志时遇到问题,通过降低Lombok版本(从1.18.x降至1.16.10)解决了问题... 目录@Slf4androidj注解,log.info()无法使用问题最后解决总结@Slf4j注解,log.info(

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06