最近开发项目时候发现,有时候因为网络或者个人问题,会出现重复点击提交按钮的情况,这样有可能会在数据库生成两条数据,造成数据混淆。今天来谈一下如何解决这个问题。
搭建springboot项目
1. 选择新建项目
2. 选择Spring Initializr
3. 填写相关信息
4. 选择web依赖
5. 选择项目位置
开始代码书写啦
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. 定义一个异常类
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
;
@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
@Configuration
public class NoRepeatSubmitAop {
private final Log logger
= LogFactory
.getLog(getClass());
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();
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
;
@RestController
@RequestMapping("/test")
public class TestController {
@NoRepeatSubmit
@RequestMapping("/one")
public RestMessage
test(){
return new RestMessage(0, "测试通过");
}
}
第一次点击返回正常信息: 快速点击第二次会出现错误信息:
测试通过!
源代码地址为:
https://github.com/zhouzhaodong/springboot/tree/master/repeat-submission
个人博客地址:
http://www.zhouzhaodong.xyz/