扩展一个接口防重复调用功能,使用Idempotent注解可以实现
This commit is contained in:
parent
077ceba48c
commit
b300e5d47b
@ -224,6 +224,14 @@
|
|||||||
<artifactId>TarsosDSP</artifactId>
|
<artifactId>TarsosDSP</artifactId>
|
||||||
<version>2.4</version>
|
<version>2.4</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.redisson/redisson-spring-boot-starter -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.redisson</groupId>
|
||||||
|
<artifactId>redisson-spring-boot-starter</artifactId>
|
||||||
|
<version>3.40.2</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
package com.yfd.platform.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: zcg
|
||||||
|
* @Description: 幂等注解
|
||||||
|
* @Date: 2024/3/12
|
||||||
|
**/
|
||||||
|
@Target({ElementType.METHOD})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface Idempotent {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 幂等的超时时间,默认为 1 秒
|
||||||
|
*
|
||||||
|
* 注意,如果执行时间超过它,请求还是会进来
|
||||||
|
*/
|
||||||
|
int timeout() default 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间单位,默认为 SECONDS 秒
|
||||||
|
*/
|
||||||
|
TimeUnit timeUnit() default TimeUnit.SECONDS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* redis锁前缀
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
String keyPrefix() default "idempotent";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* key分隔符
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
String delimiter() default "|";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提示信息,正在执行中的提示
|
||||||
|
*/
|
||||||
|
String message() default "重复请求,请稍后重试";
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.yfd.platform.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Inherited;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 加上这个注解可以将参数设置为key
|
||||||
|
*/
|
||||||
|
@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Inherited
|
||||||
|
public @interface RequestKeyParam {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,73 @@
|
|||||||
|
package com.yfd.platform.aspect;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.yfd.platform.annotation.Idempotent;
|
||||||
|
import com.yfd.platform.utils.RequestKeyGenerator;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
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 javax.annotation.Resource;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: zcg
|
||||||
|
* @Description: 幂等切面实现
|
||||||
|
* @Date: 2024/3/12
|
||||||
|
**/
|
||||||
|
@Aspect
|
||||||
|
@Configuration
|
||||||
|
@Order(2)
|
||||||
|
@Slf4j
|
||||||
|
public class IdempotentAspect {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RedissonClient redissonClient;
|
||||||
|
|
||||||
|
@Around("execution(public * * (..)) && @annotation(com.yfd.platform.annotation.Idempotent)")
|
||||||
|
public Object interceptor(ProceedingJoinPoint joinPoint) {
|
||||||
|
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
|
||||||
|
Method method = methodSignature.getMethod();
|
||||||
|
Idempotent idempotent = method.getAnnotation(Idempotent.class);
|
||||||
|
if (StrUtil.isBlank(idempotent.keyPrefix())) {
|
||||||
|
throw new RuntimeException("重复提交前缀不能为空");
|
||||||
|
}
|
||||||
|
//获取自定义key
|
||||||
|
final String lockKey = RequestKeyGenerator.getLockKey(joinPoint);
|
||||||
|
// 使用Redisson分布式锁的方式判断是否重复提交
|
||||||
|
RLock lock = redissonClient.getLock(lockKey);
|
||||||
|
boolean isLocked = false;
|
||||||
|
try {
|
||||||
|
//尝试抢占锁
|
||||||
|
isLocked = lock.tryLock();
|
||||||
|
//没有拿到锁说明已经有了请求了
|
||||||
|
if (!isLocked) {
|
||||||
|
throw new RuntimeException(idempotent.message());
|
||||||
|
}
|
||||||
|
//拿到锁后设置过期时间
|
||||||
|
lock.lock(idempotent.timeout(), idempotent.timeUnit());
|
||||||
|
try {
|
||||||
|
return joinPoint.proceed();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
log.info("系统异常,", throwable);
|
||||||
|
throw new RuntimeException("系统异常," + throwable.getMessage());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
} finally {
|
||||||
|
//释放锁
|
||||||
|
if (isLocked && lock.isHeldByCurrentThread()) {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -2,6 +2,7 @@ package com.yfd.platform.modules.algorithm.controller;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.yfd.platform.annotation.Idempotent;
|
||||||
import com.yfd.platform.config.ResponseResult;
|
import com.yfd.platform.config.ResponseResult;
|
||||||
import com.yfd.platform.modules.algorithm.domain.AlgorithmLogs;
|
import com.yfd.platform.modules.algorithm.domain.AlgorithmLogs;
|
||||||
import com.yfd.platform.modules.algorithm.service.IAlgorithmLogsService;
|
import com.yfd.platform.modules.algorithm.service.IAlgorithmLogsService;
|
||||||
@ -45,6 +46,7 @@ public class AlgorithmLogsController {
|
|||||||
// 算法分类,区域,间隔,部件
|
// 算法分类,区域,间隔,部件
|
||||||
@PostMapping("/testQueryWrapperBuilder")
|
@PostMapping("/testQueryWrapperBuilder")
|
||||||
@ApiOperation("测试构造器")
|
@ApiOperation("测试构造器")
|
||||||
|
@Idempotent
|
||||||
public ResponseResult testQueryWrapperBuilder(@RequestBody QueryCondition queryCondition) {
|
public ResponseResult testQueryWrapperBuilder(@RequestBody QueryCondition queryCondition) {
|
||||||
Page<AlgorithmLogs> page = new Page<>();
|
Page<AlgorithmLogs> page = new Page<>();
|
||||||
page.setSize(queryCondition.getSize());
|
page.setSize(queryCondition.getSize());
|
||||||
|
@ -0,0 +1,76 @@
|
|||||||
|
package com.yfd.platform.utils;
|
||||||
|
|
||||||
|
import com.yfd.platform.annotation.Idempotent;
|
||||||
|
import com.yfd.platform.annotation.RequestKeyParam;
|
||||||
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
|
import org.aspectj.lang.reflect.MethodSignature;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: zcg
|
||||||
|
* @Description: 生成LockKey
|
||||||
|
* @Date: 2024/3/12
|
||||||
|
**/
|
||||||
|
public class RequestKeyGenerator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取LockKey
|
||||||
|
*
|
||||||
|
* @param joinPoint 切入点
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getLockKey(ProceedingJoinPoint joinPoint) {
|
||||||
|
//获取连接点的方法签名对象
|
||||||
|
MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
|
||||||
|
//Method对象
|
||||||
|
Method method = methodSignature.getMethod();
|
||||||
|
//获取Method对象上的注解对象
|
||||||
|
Idempotent idempotent = method.getAnnotation(Idempotent.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(idempotent.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(idempotent.delimiter()).append(ReflectionUtils.getField(field, object));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//返回指定前缀的key
|
||||||
|
return idempotent.keyPrefix() + sb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user