中心思想
每次客户端调用接口向redis中存储唯一的key值,执行完添加后删除掉改key值
boolean flag = redisService.setIfAbsent("key值", "标书", 过期时间, 过期时间单位);
如果redis中已经存在 key,则返回fasle
如果redis中没有key,设置key值并返回true
可单独使用,也可在starter公共组件编写统一的注解,通过AOP思想实现,代码如下
引入pom依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
String moduleName() default ""; // 模块名称
long expire() default 3; // 过期时间,默认3秒
}
编写实现的AOP
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
public class IdempotentAspect {
@Autowired
private RedisService redisService;
@Pointcut("@annotation(idempotent)")
public void idempotentPointcut(Idempotent idempotent) {
}
@Around("@annotation(idempotent)")
public Object around(ProceedingJoinPoint joinPoint, Idempotent idempotent) throws Throwable {
// 获取当前登录用户信息
LoginUser loginUser = UserContextHolder.getCurrentUser();
// 构造幂等性部分key
String idempotentKey = idempotent.moduleName() + ":" + loginUser.getId();
// 获取完整key
String redisKey = RedisKeyCommonUtils.getIdempotentKey(idempotentKey);
boolean flag = redisService.setIfAbsent(redisKey , "used", idempotent.expire(), TimeUnit.SECONDS);
// 检查Redis中是否存在此键
if (!flag) {
return Response.failed400("请勿频繁操作!");
}
try {
// 继续执行目标方法
return joinPoint.proceed();
} finally {
// 删除幂等性键
redisService.deleteObject(redisKey);
}
}
}
使用
// 未传入 expire 默认是 3s, 传入则使用传入的值
// @Idempotent(moduleName = "add", expire = 5)
@Idempotent(moduleName = "add")
public Response<?> add(Object obj){
service.add(obj)
}