今天早上,新來的同事小王忽然問我:「周哥,什麼是冪等性啊?」。而後我就跟他解釋了一番,冪等性就是說不管你執行幾回請求,其結果是同樣的。說到了冪等就不得不說重複提交了,你連續點擊提交按鈕,理論上來講這是同一條數據,數據庫應該只能存入一條,而實際上存放了多條,這就違反了冪等性。所以咱們就須要作一些處理,來保證連續點擊提交按鈕後,數據庫只能存入一條數據。java
防止重複提交的方式不少,這裏我就說一下我認爲比較好用的一種。redis
咱們經過獲取用戶ip及訪問的接口來判斷他是否重複提交,假如這個ip在一段時間內容屢次訪問這個接口,咱們則認爲是重複提交,咱們將重複提交的請求直接處理便可,不讓訪問目標接口。數據庫
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface NoRepeatSubmit { /** * 默認1s鍾之內算重複提交 * @return */ long timeout() default 1; }
咱們將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(); } }
在接口上添加上咱們自定義的註解@NoRepeatSubmitapp
@RequestMapping("/test") @NoRepeatSubmit public String tt(HttpServletRequest request) { return "1"; }
咱們在瀏覽器中連續請求兩次接口。發現第一次接口響應正常內容:1,第二次接口響應了不可重複提交的異常信息。1s以後再點擊接口,發現又響應了正常內容。dom
至此,這種防止重複提交的方式就介紹完了,這樣咱們就完美防止了接口重複提交。ide