文章目录

  • 前言
  • 一、实现思路
  • 二、利用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();
	}