文章目录
- 前言
- 一、实现思路
- 二、利用aop自定义注解实现
- 1.新增一个自定义注解
- 2.实现自定义注解
- 总结
前言
用于 接口防刷, 重复提交等;
一、实现思路
基于redis 的过期key 实现,一段时间内的防止重复提交
二、利用aop自定义注解实现
1.新增一个自定义注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface NoRepeatSubmit {
boolean required() default true;
}
2.实现自定义注解
思路: 根据入参 Hash 之后转为 字符串为key,存入redis,设置失效时间,则这段时间断内再次提交,则为重复提交,直接不处理
@Aspect
@Component
@Slf4j
public class NoRepeatSubmitAspect {
@Autowired
private StringRedisTemplate redisTemplate;
@Pointcut("@annotation(com.example.test.config.NoRepeatSubmit)")
public void noRepeatSubmit() {
}
@Around(value = "noRepeatSubmit()")
public Object checkRepeatSubmit(ProceedingJoinPoint jp) throws Throwable {
// 获取请求参数
String paramJSON = JSONUtil.toJsonStr(jp.getArgs());
// 计算请求参数hash值
String hash = String.valueOf(HashUtil.fnvHash(paramJSON));
// 获取到分布式锁,如果获取不到,说明重复提交,直接丢弃
String value = RandomUtil.randomNumbers(10);
Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(hash, value,500, TimeUnit.MILLISECONDS);
if (aBoolean) {
log.info("获取到锁:{}", hash);
Object reuslt = jp.proceed();
String lockValue = redisTemplate.opsForValue().get(hash);
if (ObjectUtil.equals(lockValue,value)) {
redisTemplate.delete(hash);
}
log.info("释放锁锁:{}", hash);
return reuslt;
}
System.out.println("未执行");
return null;
}
public HttpServletRequest getRequest() {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
//从获取RequestAttributes中获取HttpServletRequest的信息
HttpServletRequest httpRequest = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
return httpRequest;
}
}
利用redis 过期key实现
重点,重点,重点: 锁释放的时候,一定要判断,否则会将别人的锁释放,造成判断错误
当然可以继续扩展,比如根据其他 ip 用户 等,还可以根据次数进行锁定, 思路依然是 redis 过期key
第一次访问正常通过,连续点击,那么之后的请求就不会再次被处理了(甚至可以在检测到第二次为刷新的时候,延长key时间,不停刷新,不停延长,一直不处理)
总结
自定义注解写了好几篇了,都是一个套路,大家应该都学废了,不出意外,这应该是最后一个自定义注解的文章了
@SneakyThrows
@Around("@within(noRepeatSubmit) || @annotation(noRepeatSubmit)")
public Object checkRepeatSubmit(ProceedingJoinPoint point, NoRepeatSubmit noRepeatSubmit) {
// @annotation 方法上的@NoRepeatSubmit
// @within若方法上没有@NoRepeatSubmit注解,则获取类上的@NoRepeatSubmit
return point.proceed();
}