最近开发项目时候发现,有时候因为网络或者个人问题,会出现重复点击提交按钮的情况,这样有可能会在数据库生成两条数据,造成数据混淆。今天来谈一下如何解决这个问题。
<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>
/** * 返回信息 * * @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; } }
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 { }
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, "验证重复提交时出现未知异常!"); } } }
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, "测试通过"); } }
第一次点击返回正常信息:
快速点击第二次会出现错误信息:
测试通过!
https://github.com/zhouzhaodo...
http://www.zhouzhaodong.xyz/