赞
踩
环境:
java 17
spring boot v-3.1.3
redisson-spring-data-30 v-3.20.1
描述
利用方法注解+aop+redisson锁使用用户id+接口做key来做防连续调用。
redisson配置
import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; /** * @author: dzg * @date: 2023/2/23 16:14 * @describe: redisson配置 */ @Configuration public class RedissonConfig { @Bean public RedissonClient redissonClient() { Config config = new Config(); config.useSingleServer() //地址 .setAddress("redis://127.0.0.1:6379") .setDatabase(0) .setPassword("123456") ; return Redisson.create(config); } }
自定义注解AntiDuplicate
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author: dzg * @date: 2023/4/21 13:42 * @describe: 防连续请求注解 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AntiDuplicate { /** * 某用户某接口中间间隔多少秒才让进行下次请求,默认 3s * @return 单位:秒 */ int value() default 3; }
注解具体实现
```java import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; /** * @author: dzg * @date: 2023/4/21 13:46 * @describe: {@link AntiDuplicate} 防连续点击实现 */ @Aspect @Component @Order(-1) public class AntiDuplicateAop { /** 打印 log 日志 */ private static final Logger log = LoggerFactory.getLogger(AntiDuplicateAop.class); @Resource private RedissonClient redissonClient; /** * 获取注解 * @Pointcut("@annotation(注解的路径)") */ @Pointcut("@annotation(com.dzg.common.annotation.AntiDuplicate)") public void antiDuplicateAspect() { } /** * 对切点方法进行前置增强 * @param joinPoint joinPoint */ @Before("antiDuplicateAspect() && @annotation(antiDuplicate)") public void doBefore(JoinPoint joinPoint, AntiDuplicate antiDuplicate) throws InterruptedException { // ContextUtil.getRequest() 项目里边获取 HttpServletRequest 的方法,这里用来取出每个登录用户的token HttpServletRequest request = ContextUtil.getRequest(); // 这个用户的token+请求的url String redisLockKey = request.getHeader("dzg-token")+request.getRequestURI(); // 获取锁 RLock rLock = redissonClient.getLock(redisLockKey); if (rLock.isLocked()) { // 锁被占用后的处理结果 mutualExclusion(redisLockKey); } int lockTime = antiDuplicate.value() <= 0 ? 15 : antiDuplicate.value(); // 上锁,参数分别为:获取不到锁时候等待多久,锁多少时间自动释放,单位(秒) boolean isLock = rLock.tryLock(0, lockTime, TimeUnit.SECONDS); if (!isLock) { //锁被占用后的处理结果 mutualExclusion(redisLockKey); } } /** * 锁被占用后的处理结果 * */ private void mutualExclusion(){ // 自定义异常因为项目有全局拦截(全局异常拦截看其他文章) throw new CustomException(ResultCode.CODE_400001026); //ResultCode.CODE_400001026(操作频繁,请稍后再试) } }
使用示例
@Operation(summary = "示例一使用默认时长")
@AntiDuplicate
@PostMapping("/test1")
public Result<Object> test1(){
return Result.success();
}
@Operation(summary = "示例二自定义时长1秒")
@AntiDuplicate(1)
@PostMapping("/test2")
public Result<Object> test2(){
return Result.success();
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。