From 07b0b9bbd5fbafeda070314cdfef22f7db6d4444 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E8=9C=97=E7=89=9B?=
<1003271829@qq.com>
Date: Fri, 5 Jan 2024 23:39:52 +0800
Subject: [PATCH] =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E9=98=B2=E6=8A=96=20?=
=?UTF-8?q?=E5=88=86=E5=B8=83=E5=BC=8F=E9=94=81=E4=BB=A5=E5=8F=8Aredis?=
=?UTF-8?q?=E7=BC=93=E5=AD=98=E5=AE=9E=E7=8E=B0=E6=96=B9=E6=A1=88=E9=83=BD?=
=?UTF-8?q?=E6=9C=89=E5=93=A6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../pom.xml | 6 +
.../com/woniu/demo/config/RedissonConfig.java | 22 +++
.../redisson/RedissonRequestLockAspect.java | 126 ++++++++++++++++++
3 files changed, 154 insertions(+)
create mode 100644 接口防抖防重复提交/woniu-springboot-interface-demo-master/src/main/java/com/woniu/demo/config/RedissonConfig.java
create mode 100644 接口防抖防重复提交/woniu-springboot-interface-demo-master/src/main/java/com/woniu/demo/redisson/RedissonRequestLockAspect.java
diff --git a/接口防抖防重复提交/woniu-springboot-interface-demo-master/pom.xml b/接口防抖防重复提交/woniu-springboot-interface-demo-master/pom.xml
index 071937b..bc3d89d 100644
--- a/接口防抖防重复提交/woniu-springboot-interface-demo-master/pom.xml
+++ b/接口防抖防重复提交/woniu-springboot-interface-demo-master/pom.xml
@@ -23,6 +23,12 @@
spring-boot-starter-web
+
+ org.redisson
+ redisson-spring-boot-starter
+ 3.10.6
+
+
mysql
diff --git a/接口防抖防重复提交/woniu-springboot-interface-demo-master/src/main/java/com/woniu/demo/config/RedissonConfig.java b/接口防抖防重复提交/woniu-springboot-interface-demo-master/src/main/java/com/woniu/demo/config/RedissonConfig.java
new file mode 100644
index 0000000..3d9993d
--- /dev/null
+++ b/接口防抖防重复提交/woniu-springboot-interface-demo-master/src/main/java/com/woniu/demo/config/RedissonConfig.java
@@ -0,0 +1,22 @@
+package com.woniu.demo.config;
+
+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;
+
+@Configuration
+public class RedissonConfig {
+
+ @Bean
+ public RedissonClient redissonClient() {
+ Config config = new Config();
+ // 这里假设你使用单节点的Redis服务器
+ config.useSingleServer()
+ // 使用与Spring Data Redis相同的地址
+ .setAddress("redis://127.0.0.1:6379");
+ return Redisson.create(config);
+ }
+}
+
diff --git a/接口防抖防重复提交/woniu-springboot-interface-demo-master/src/main/java/com/woniu/demo/redisson/RedissonRequestLockAspect.java b/接口防抖防重复提交/woniu-springboot-interface-demo-master/src/main/java/com/woniu/demo/redisson/RedissonRequestLockAspect.java
new file mode 100644
index 0000000..aa102da
--- /dev/null
+++ b/接口防抖防重复提交/woniu-springboot-interface-demo-master/src/main/java/com/woniu/demo/redisson/RedissonRequestLockAspect.java
@@ -0,0 +1,126 @@
+package com.woniu.demo.redisson;
+
+import com.woniu.demo.annotation.RequestKeyParam;
+import com.woniu.demo.annotation.RequestLock;
+import com.woniu.demo.exception.biz.BizException;
+import com.woniu.demo.model.response.ResponseCodeEnum;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.util.ReflectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+
+/**
+ * 接口优化,如何接口防抖(防重复提交)?
+ * 分布式锁实现方案!
+ * @description
+ */
+@Aspect
+@Configuration
+@Order(2)
+public class RedissonRequestLockAspect {
+
+ private RedissonClient redissonClient;
+
+ @Autowired
+ public RedissonRequestLockAspect(RedissonClient redissonClient) {
+ this.redissonClient = redissonClient;
+ }
+
+ @Around("execution(public * * (..)) && @annotation(com.summo.demo.config.requestlock.RequestLock)")
+ public Object interceptor(ProceedingJoinPoint joinPoint) {
+ MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
+ Method method = methodSignature.getMethod();
+ RequestLock requestLock = method.getAnnotation(RequestLock.class);
+ if (StringUtils.isEmpty(requestLock.prefix())) {
+ throw new BizException(ResponseCodeEnum.BIZ_CHECK_FAIL, "重复提交前缀不能为空");
+ }
+ //获取自定义key
+ final String lockKey = getLockKey(joinPoint);
+ // 使用Redisson分布式锁的方式判断是否重复提交
+ RLock lock = redissonClient.getLock(lockKey);
+ boolean isLocked = false;
+ try {
+ //尝试抢占锁
+ isLocked = lock.tryLock();
+ //没有拿到锁说明已经有了请求了
+ if (!isLocked) {
+ throw new BizException(ResponseCodeEnum.BIZ_CHECK_FAIL, "您的操作太快了,请稍后重试");
+ }
+ //拿到锁后设置过期时间
+ lock.lock(requestLock.expire(), requestLock.timeUnit());
+ try {
+ return joinPoint.proceed();
+ } catch (Throwable throwable) {
+ throw new BizException(ResponseCodeEnum.BIZ_CHECK_FAIL, "系统异常");
+ }
+ } catch (Exception e) {
+ throw new BizException(ResponseCodeEnum.BIZ_CHECK_FAIL, "您的操作太快了,请稍后重试");
+ } finally {
+ //释放锁
+ if (isLocked && lock.isHeldByCurrentThread()) {
+ lock.unlock();
+ }
+ }
+
+ }
+
+ public String getLockKey(ProceedingJoinPoint joinPoint) {
+ //获取连接点的方法签名对象
+ MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
+ //Method对象
+ Method method = methodSignature.getMethod();
+ //获取Method对象上的注解对象
+ RequestLock requestLock = method.getAnnotation(RequestLock.class);
+ //获取方法参数
+ final Object[] args = joinPoint.getArgs();
+ //获取Method对象上所有的注解
+ final Parameter[] parameters = method.getParameters();
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < parameters.length; i++) {
+ final RequestKeyParam keyParam = parameters[i].getAnnotation(RequestKeyParam.class);
+ //如果属性不是RequestKeyParam注解,则不处理
+ if (keyParam == null) {
+ continue;
+ }
+ //如果属性是RequestKeyParam注解,则拼接 连接符 "& + RequestKeyParam"
+ sb.append(requestLock.delimiter()).append(args[i]);
+ }
+ //如果方法上没有加RequestKeyParam注解
+ if (StringUtils.isEmpty(sb.toString())) {
+ //获取方法上的多个注解(为什么是两层数组:因为第二层数组是只有一个元素的数组)
+ final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
+ //循环注解
+ for (int i = 0; i < parameterAnnotations.length; i++) {
+ final Object object = args[i];
+ //获取注解类中所有的属性字段
+ final Field[] fields = object.getClass().getDeclaredFields();
+ for (Field field : fields) {
+ //判断字段上是否有RequestKeyParam注解
+ final RequestKeyParam annotation = field.getAnnotation(RequestKeyParam.class);
+ //如果没有,跳过
+ if (annotation == null) {
+ continue;
+ }
+ //如果有,设置Accessible为true(为true时可以使用反射访问私有变量,否则不能访问私有变量)
+ field.setAccessible(true);
+ //如果属性是RequestKeyParam注解,则拼接 连接符" & + RequestKeyParam"
+ sb.append(requestLock.delimiter()).append(ReflectionUtils.getField(field, object));
+ }
+ }
+ }
+ //返回指定前缀的key
+ return requestLock.prefix() + sb;
+ }
+}