Java實現防重複提交

歡迎訪問我的個人博客:www.ifueen.com

防重複提交的重要性?

在業務開發中,爲什麼我們要去想辦法解決重複提交這一問題發生?網上的概念很多:導致表單重複提交,造成數據重複,增加服務器負載,嚴重甚至會造成服務器宕機,那麼爲什麼會造成這種現象?前臺操作的抖動,快速操作,網絡通信或者後端響應慢,都會增加後端重複處理的概率,就拿我親身經歷來說,因爲業務邏輯,需要進行一個"關注"操作,但是寫好業務之後在測試時連續點擊幾下,重複地進行關注和取消關注操作,因爲操作過於頻繁,而服務器走過來的響應速度沒有那麼快地進行處理,導致重複數據插入地情況,最後導致在查詢關注的時候服務器報錯,這個時候,放重複提交就顯得很重要了

如何防重複提交?

其實實現地方法有很多,但是原理大概都是相通的,我選擇的是通過使用AOP+redis來進行處理,前端發起請求的時候需要在請求頭裏將token給我,然後我這邊通過token+ip再加上請求的路徑作爲一個key存到redis裏面去,設置一個合適的過期時間,下一次再從redis中取出和當前時間進行一個判斷,如果大於我們設定的一個超時時間,那麼就進行攔截,不讓它進行下面的業務代碼,並給出提示“操作頻繁,請稍後重試”

代碼實現

前提環境準備:SpringAOP的支持,Redis的支持

其實用文字敘述出來感覺有一點點繞,但是代碼實現起來其實不難

首先我們需要自定義一個註解:

package com.ifueen.anntion;

import org.springframework.stereotype.Component;

import java.lang.annotation.*;

/**
 * @author fueen
 * @date 2020/7/4
 * 自定義防重複提交註解
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface NoRepeatSubmit {
    /**
     * 默認時間  默認1秒鐘
     * @return
     */
    int lockTime() default 1000;
}

然後寫一個AOP進行攔截處理

package com.ifueen.aspect;

import com.ifueen.anntion.NoRepeatSubmit;
import com.ifueen.utils.CommonResult;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
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;

/**
 * @author fueen
 * @date 2020/7/4 15:07
 */
@Aspect
@Component
@Slf4j
@SuppressWarnings("all")
public class RepeatSubmitAspect {
    public static final String  KEYPREX="noRpeat:user:";

    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 進行接口防重複操作處理
     * @param pjp
     * @param noRepeatSubmit
     * @return
     */
    @Around("execution(* com.ifueen.controller.*.*(..)) && @annotation(noRepeatSubmit)")
    public Object around(ProceedingJoinPoint pjp, NoRepeatSubmit noRepeatSubmit) throws Throwable {
        try {
            //獲取request
            HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
            //拿到token和請求路徑
            StringBuilder sb = new StringBuilder();
            sb.append(KEYPREX).append(request.getHeader("token").toString()).append(request.getRequestURI().toString());
            //獲取現在時間
            long now = System.currentTimeMillis();
            if (redisTemplate.hasKey(sb.toString())){
                //上次請求時間
                long lastTime= Long.valueOf(redisTemplate.opsForValue().get(sb.toString()).toString()) ;
                // 如果現在距離上次提交時間小於設置的默認時間 則 判斷爲重複提交  否則 正常提交 -> 進入業務處理
                if ((now - lastTime)>noRepeatSubmit.lockTime()){
                    //重新記錄時間 10分鐘過期時間
                    redisTemplate.opsForValue().set(sb.toString(),String.valueOf(now),10, TimeUnit.MINUTES);
                    //處理業務
                    Object result =  pjp.proceed();
                    return result;
                }else {
                    return CommonResult.getFaiInstance("-1","點擊的太快了,請慢一點!");
                }
            }else {
                //第一次操作
                redisTemplate.opsForValue().set(sb.toString(),String.valueOf(now),10, TimeUnit.MINUTES);
                Object result =  pjp.proceed();
                return result;
            }
        }catch (Throwable e){
            log.error("校驗表單重複提交時異常: {}", e.getMessage());
            return CommonResult.getFaiInstance("-1","校驗重複提交時異常");
        }
    }

}

AOP和註解都寫好了,現在只需要在要用到的請求上面打上這個註解就可以了

在這裏插入圖片描述

其他請求需要進行防重複提交,也只需要打上這個註解即可

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章