Java中如何避免重复提交请求(不使用Redis/hazelcast等缓存)

Java中如何避免重复提交请求

一、借助本地锁实现

这种方式主要通过自定义注解、springaop、guavacache来生成本地锁,达到防止重复提交的效果。

二、具体实现

1.引入guava依赖

Guava是谷歌开源的Java库,这个库提供用于集合,缓存,支持原语,并发性,常见注解,字符串处理,I/O和验证的实用方法,对JDK工具做了很好扩展。

<dependency>
	<groupId>com.google.guava</groupId>
	<artifactId>guava</artifactId>
	<version>21.0</version>
</dependency>

2.自定义RepeatSubmit注解
编写自定义注解,用于需要控制重复提交的方法上。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface RepeatSubmit {

    String key() default "";

}

3.自定义注解切面
编写自定义注解的aop拦截器具体实现,读取有RepeatSubmit注解的方法,解析注解中定义的key值在本地缓存中是否存在,若存在则提示重复请求,若为第一次请求则将key存入本地缓存中。

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.apache.commons.lang3.StringUtils;
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.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * Author:Qinggq
 * Date:2023/6/27
 * Description:
 */
@Aspect
@Component
public class RepeatSubmitAspect {

    private static final Cache<String,Object> CACHES = CacheBuilder.newBuilder()
            // 最大缓存 设置为1000个
            .maximumSize(1000)
            // 设置写缓存后5s过期
            .expireAfterWrite(5, TimeUnit.SECONDS)
            .build();

    @Around("execution(public * *(..)) && @annotation(com.xxx.Annotation.RepeatSubmit))")
    public Object interceptor(ProceedingJoinPoint pjp){
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        RepeatSubmit repeatSubmit = method.getAnnotation(RepeatSubmit.class);
        String key = getKey(repeatSubmit.key(),pjp.getArgs());
        if (StringUtils.isNotBlank(key)){
            if (CACHES.getIfPresent(key) != null){
                throw new RuntimeException("请勿重复请求");
            }
            // 如果是第一次请求,就将key 当前对象压入缓存中
            CACHES.put(key,key);
        }

        try {
            return pjp.proceed();
        } catch (Throwable throwable) {
            throw new RuntimeException("服务器异常!");
        }
    }

    /**
     * key 的生成策略
     * @param keyExpress 表达式
     * @param args 参数
     * @return 生成的key
     */
    private String getKey(String keyExpress, Object[] args) {
        for (int i = 0; i < args.length; i++) {
            keyExpress = keyExpress.replace("arg[" + i +"]",args[i].toString());
        }
        return keyExpress;
    }
}

4.控制层实现
在需要限制重复提交的方法上加入@RepeatSubmit注解,其中key值为自定义的存入缓存中的key。

@GetMapping("/query")
//@RepeatSubmit(key = "query:arg[0]") // 参数可以动态获取作为key
@RepeatSubmit(key = "产品数据信息实时同步查询") //也可写死查询的key
public ResultModel query(@RequestParam String token){
	try{
		// 模拟请求执行时间2s
		Thread.sleep(2000);
	}catch{
		e.printStackTrace();
	}
	return ResultModel.success();
}

5.效果展示
启动应用,访问上面的/query请求查看效果。
正常访问一下,结果如下:
在这里插入图片描述
接下来,在正常访问过程中,重复点击提交,可以看到已达到限制效果。
在这里插入图片描述

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值