Spring Boot 使用自定义注解实现防止表单重复提交

使用自定义注解实现防止表单重复提交我们还是先引入 Maven 依赖
<!-- Spring框架基本的核心工具 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId></dependency><!-- SpringWeb模块 --><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId></dependency><!-- 自定义验证注解 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><!--常用工具类 --><dependency><groupId>org.Apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><!-- io常用工具类 --><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId> </dependency><!-- JSON工具类 --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></dependency><!-- 阿里JSON解析器 --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId></dependency><!-- servlet包 --><dependency><groupId>JAVAx.servlet</groupId><artifactId>javax.servlet-api</artifactId></dependency>复制代码接下来我们实现一个注解类(注解在 Java 中与类、接口的声明类似,只是所使用的关键字有所不同,声明注解使用 @interface 关键字 。在底层实现上,所有定义的注解都会自动继承
java.lang.annotation.Annotation 接口 。)
import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * 自定义注解防止表单重复提交 */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface RepeatSubmit{/*** 间隔时间(ms),小于此时间视为重复提交*/public int interval() default 5000;/*** 提示消息*/public String message() default "不允许重复提交,请稍后再试";}复制代码P.S. 关于自定义注解的解释在之前的博客中有写到,这里就不再多说了,有需要了解的小伙伴请移步至:大聪明教你学Java | Spring Boot 使用自定义注解实现操作日志的记录
下面我们再实现一个拦截器,对提交表单的过程做一个相应的拦截校验
import java.lang.reflect.Method;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.springframework.stereotype.Component;import org.springframework.web.method.HandlerMethod;import org.springframework.web.servlet.HandlerInterceptor;/** * 防止重复提交拦截器 */@Componentpublic abstract class RepeatSubmitInterceptor implements HandlerInterceptor{@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{if (handler instanceof HandlerMethod){HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);if (annotation != null){if (this.isRepeatSubmit(request, annotation)){//如果本次提交被认为是重复提交,则在此处做具体的逻辑处理//如:弹出警告窗口等return false;}}return true;}else{return true;}}/*** 验证是否重复提交由子类实现具体的防重复提交的规则** @param request 请求对象* @param annotation 防复注解* @return 结果*/public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) throws Exception;}复制代码我们可以看到再拦截器中一共有两个方法,分别是 preHandle 和 isRepeatSubmit 。无论我们执行什么请求,都会进入 RepeatSubmitInterceptor 拦截器,进入拦截器后先执行 preHandle 方法进行预处理,判断本次拦截的方法是否增加了 RepeatSubmit 自定义注解,如果增加了该注解才会进行具体的校验 。isRepeatSubmit 方法是防止表单重复提交的规则,我们通过子类来实现 isRepeatSubmit 具体的校验规则
import java.util.HashMap;import java.util.Map;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;import com.mumu.framework.interceptor.RepeatSubmitInterceptor;import org.springframework.stereotype.Component;import com.mumu.common.annotation.RepeatSubmit;import com.xxx.xxx.xxx.JSON; /** * 判断请求url和数据是否和上一次相同 * com.xxx.xxx.xxx.JSON; 是自定义的json工具类,具体代码贴在后面 */@Componentpublic class SameUrlDataInterceptor extends RepeatSubmitInterceptor{public final String REPEAT_PARAMS = "repeatParams";public final String REPEAT_TIME = "repeatTime";public final String SESSION_REPEAT_KEY = "repeatData";@SuppressWarnings("unchecked")@Overridepublic boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) throws Exception{// 本次参数及系统时间String nowParams = JSON.marshal(request.getParameterMap());Map<String, Object> nowDataMap = new HashMap<String, Object>();nowDataMap.put(REPEAT_PARAMS, nowParams);nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());// 请求地址(作为存放session的key值)String url = request.getRequestURI();HttpSession session = request.getSession();Object sessionObj = session.getAttribute(SESSION_REPEAT_KEY);if (sessionObj != null){Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;if (sessionMap.containsKey(url)){Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval())){return true;}}}Map<String, Object> sessionMap = new HashMap<String, Object>();sessionMap.put(url, nowDataMap);session.setAttribute(SESSION_REPEAT_KEY, sessionMap);return false;}/*** 判断参数是否相同*/private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap){String nowParams = (String) nowMap.get(REPEAT_PARAMS);String preParams = (String) preMap.get(REPEAT_PARAMS);return nowParams.equals(preParams);}/*** 判断两次间隔时间*/private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap, int interval){long time1 = (Long) nowMap.get(REPEAT_TIME);long time2 = (Long) preMap.get(REPEAT_TIME);if ((time1 - time2) < interval){return true;}return false;}}复制代码


推荐阅读