SpringBoot处理短时间内重复提交

文章介绍了如何在SpringBoot应用中利用AOP切面和Redis来防止短时间内用户频繁访问同一接口,从而避免数据异常和减轻服务器压力。首先添加相关依赖,然后自定义注解`@RepeatSubmit`,接着创建一个重复请求拦截器,通过Redis存储请求URI来判断是否为重复请求,并在全局异常处理类中处理重复请求异常。
摘要由CSDN通过智能技术生成

为了解决用户短时间内频繁访问一个接口,造成可能出现的数据异常或者为了减轻服务器的压力。必须对短时间内的频繁请求进行处理。

  1. 添加用到的依赖

<!--aop-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--redis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 自定义注解

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit {

    /**
     * 默认失效时间2秒
     * @return
     */
    long seconds() default 2;
}
  1. 自定义一个重复提交异常类

public class RepeatSubmitException extends RuntimeException{

    public RepeatSubmitException(String msg){
        super(msg);
    }
}
  1. 创建一个重复请求拦截器

import com.example.demo.annotation.RepeatSubmit;
import com.example.demo.exception.RepeatSubmitException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Component
public class RepeatSubmitInterceptor implements HandlerInterceptor {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws RepeatSubmitException {
        if (handler instanceof HandlerMethod) {
            //只拦截标注了@RepeatSubmit该注解
            HandlerMethod method = (HandlerMethod) handler;
            //标注在方法上的@RepeatSubmit
            RepeatSubmit repeatSubmitByMethod =
                    AnnotationUtils.findAnnotation(method.getMethod(), RepeatSubmit.class);
            //标注在controler类上的@RepeatSubmit
            RepeatSubmit repeatSubmitByCls =
                    AnnotationUtils.findAnnotation(method.getMethod().getDeclaringClass(), RepeatSubmit.class);
            //没有限制重复提交,直接跳过
            if (Objects.isNull(repeatSubmitByMethod) && Objects.isNull(repeatSubmitByCls))
                return true;
            // todo: 组合判断条件,这里仅仅是演示,实际项目中根据架构组合条件
            //请求的URI
            String uri = request.getRequestURI();
            //存在即返回false,不存在即返回true
            Boolean ifAbsent = stringRedisTemplate.opsForValue().setIfAbsent(uri, "",
                    Objects.nonNull(repeatSubmitByMethod) ? repeatSubmitByMethod.seconds() : repeatSubmitByCls.seconds(),
                    TimeUnit.SECONDS);
            //如果存在,表示已经请求过了,直接抛出异常,由全局异常进行处理返回指定信息
            if (ifAbsent != null && !ifAbsent) {
                throw new RepeatSubmitException("重复请求");
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}
  1. 创建统一结果返回类

@Getter
@Setter
public class ResultResponse {

    String msg;

    public ResultResponse(String msg){
        this.msg = msg;
    }
}
  1. 创建全局统一异常处理类 处理重复请求异常

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     *  重复请求的异常
     * @param ex
     * @return
     */
    @ExceptionHandler(RepeatSubmitException.class)
    public ResultResponse onException(RepeatSubmitException ex){
        log.error("错误异常:"+ex.getMessage());

        return new ResultResponse("请勿重复提交");
    }
}
  1. 测试

@RestController
//类上标注了@RepeatSubmit注解,该类全部请求都需要拦截
//@RepeatSubmit
public class LoginController {

    /**
     * 方法上标注@RepeatSubmit注解 只针对该请求进行拦截
     */
    @RepeatSubmit
    @GetMapping("/login")
    public String toLogin(){
        return "login success";
    }

}
  1. 结果正常情况

错误情况

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qinxun2008081

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值