今天早上,新來的同事小王突然問我:“周哥,什么是冪等性啊?”,然后我就跟他解釋了一番,冪等性就是說無論你執行幾次請求,其結果是一樣的,說到了冪等就不得不說重復提交了,你連續點擊提交按鈕,理論上來說這是同一條資料,資料庫應該只能存入一條,而實際上存放了多條,這就違反了冪等性,因此我們就需要做一些處理,來保證連續點擊提交按鈕后,資料庫只能存入一條資料,
防止重復提交的方式很多,這里我就說一下我認為比較好用的一種,
自定義注解+Aop實作
我們通過獲取用戶ip及訪問的介面來判斷他是否重復提交,假如這個ip在一段時間內容多次訪問這個介面,我們則認為是重復提交,我們將重復提交的請求直接處理即可,不讓訪問目標介面,
自定義注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NoRepeatSubmit {
/**
* 默認1s鐘以內算重復提交
* @return
*/
long timeout() default 1;
}
Aop處理邏輯
我們將ip+介面地址作為key,隨機生成UUID作為value,存入redis,每次請求進來,根據key查詢redis,如果存在則說明是重復提交,拋出例外,如果不存在,則是正常提交,將key存入redis,
@Aspect
@Component
public class NoRepeatSubmitAop {
@Autowired
private RedisService redisUtils;
/**
* 定義切入點
*/
@Pointcut("@annotation(NoRepeatSubmit)")
public void noRepeat() {}
/**
* 前置通知:在連接點之前執行的通知
* @param point
* @throws Throwable
*/
@Before("noRepeat()")
public void before(JoinPoint point) throws Exception{
// 接收到請求,記錄請求內容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Assert.notNull(request, "request can not null");
// 此處可以用token或者JSessionId
String token = IpUtils.getIpAddr(request);
String path = request.getServletPath();
String key = getKey(token, path);
String clientId = getClientId();
List<Object> lGet = redisUtils.lGet(key, 0, -1);
// 獲取注解
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
NoRepeatSubmit annotation = method.getAnnotation(NoRepeatSubmit.class);
long timeout = annotation.timeout();
boolean isSuccess = false;
if (lGet.size()==0 || lGet == null) {
isSuccess = redisUtils.lSet(key, clientId, timeout);
}
if (!isSuccess) {
// 獲取鎖失敗,認為是重復提交的請求
redisUtils.lSet(key, clientId, timeout);
throw new Exception("不可以重復提交");
}
}
private String getKey(String token, String path) {
return token + path;
}
private String getClientId() {
return UUID.randomUUID().toString();
}
}
提供介面用來測驗
在介面上添加上我們自定義的注解@NoRepeatSubmit
@RequestMapping("/test")
@NoRepeatSubmit
public String tt(HttpServletRequest request) {
return "1";
}
測驗
我們在瀏覽器中連續請求兩次介面,發現第一次介面回應正常內容:1,第二次介面回應了不可重復提交的例外資訊,1s之后再點擊介面,發現又回應了正常內容,

至此,這種防止重復提交的方式就介紹完了,這樣我們就完美防止了介面重復提交,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/158276.html
標籤:Java
