个人学习系列 – springboot防止重复提交

最近开发项目时候发现,有时候因为网络或者个人问题,会出现重复点击提交按钮的情况,这样有可能会在数据库生成两条数据,造成数据混淆。今天来谈一下如何解决这个问题。

搭建springboot项目

1. 选择新建项目

个人学习系列 - springboot防止重复提交

2. 选择Spring Initializr

个人学习系列 - springboot防止重复提交

3. 填写相关信息

个人学习系列 - springboot防止重复提交

4. 选择web依赖

个人学习系列 - springboot防止重复提交

5. 选择项目位置

个人学习系列 - springboot防止重复提交

开始代码书写啦

1. pom.xml文件依赖添加

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>29.0-jre</version>
</dependency>

2. 定义一个异常类

/**
 * 返回信息
 *
 * @author zhouzhaodong
 */
public class RestMessage {

    private int code;

    private String message;

    private Object data;

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public RestMessage(int code, String message, Object data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    public RestMessage(int code, String message) {
        this.code = code;
        this.message = message;
    }

}

3. 定义一个接口

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 定义一个注解
 * @apiNote @Target(ElementType.METHOD) 作用到方法上
 * @apiNote @Retention(RetentionPolicy.RUNTIME) 只有运行时有效
 * @author zhouzhaodong
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NoRepeatSubmit {

}

4. 定义一个切面

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

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

/**
 * 自定义一个切面类,利用aspect实现切入所有方法
 *
 * @author zhouzhaodong
 */
@Aspect
@Configuration
public class NoRepeatSubmitAop {

    private final Log logger = LogFactory.getLog(getClass());

    /**
     * 重复提交判断时间为2s
     */
    private final Cache<String, Integer> cache = CacheBuilder.newBuilder().expireAfterWrite(2L, TimeUnit.SECONDS).build();

    @Around("execution(* xyz.zhouzhaodong..*Controller.*(..)) && @annotation(nrs)")
    public Object around(ProceedingJoinPoint pjp, NoRepeatSubmit nrs) {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            String sessionId = Objects.requireNonNull(RequestContextHolder.getRequestAttributes()).getSessionId();
            assert attributes != null;
            HttpServletRequest request = attributes.getRequest();
            String key = sessionId + "-" + request.getServletPath();
            // 如果缓存中有这个url视为重复提交
            if (cache.getIfPresent(key) == null) {
                Object o = pjp.proceed();
                cache.put(key, 0);
                return o;
            } else {
                logger.error("重复提交");
                return new RestMessage(888, "请勿短时间内重复操作");
            }
        } catch (Throwable e) {
            e.printStackTrace();
            logger.error("验证重复提交时出现未知异常!");
            return new RestMessage(889, "验证重复提交时出现未知异常!");
        }

    }

}

5. 写controller进行测试

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 测试
 * @author zhouzhaodong
 */
@RestController
@RequestMapping("/test")
public class TestController {

    /**
     * 添加防重复提交注解
     * @return
     */
    @NoRepeatSubmit
    @RequestMapping("/one")
    public RestMessage test(){
        return new RestMessage(0, "测试通过");
    }

}

第一次点击返回正常信息:

个人学习系列 - springboot防止重复提交

快速点击第二次会出现错误信息:

个人学习系列 - springboot防止重复提交

测试通过!

源代码地址为:

https://github.com/zhouzhaodo…

个人博客地址:

http://www.zhouzhaodong.xyz/

原文 

https://segmentfault.com/a/1190000023075766

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » 个人学习系列 – springboot防止重复提交

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址