<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          springboot實現(xiàn)防重復(fù)提交和防重復(fù)點擊

          共 6917字,需瀏覽 14分鐘

           ·

          2020-10-02 18:47

          點擊上方藍色字體,選擇“標(biāo)星公眾號”

          優(yōu)質(zhì)文章,第一時間送達

          ? 作者?|??daleyzou

          來源 |? urlify.cn/VnAZ3y

          66套java從入門到精通實戰(zhàn)課程分享

          背景

          同一條數(shù)據(jù)被用戶點擊了多次,導(dǎo)致數(shù)據(jù)冗余,需要防止弱網(wǎng)絡(luò)等環(huán)境下的重復(fù)點擊

          目標(biāo)

          通過在指定的接口處添加注解,實現(xiàn)根據(jù)指定的接口參數(shù)來防重復(fù)點擊

          說明

          這里的重復(fù)點擊是指在指定的時間段內(nèi)多次點擊按鈕

          技術(shù)方案

          springboot + redis鎖 + 注解

          使用 feign client 進行請求測試

          最終的使用實例

          1、根據(jù)接口收到 PathVariable 參數(shù)判斷唯一

          /**
          ?????*??根據(jù)請求參數(shù)里的?PathVariable?里獲取的變量進行接口級別防重復(fù)點擊
          ?????*
          ?????*?@param?testId?測試id
          ?????*?@param?requestVo?請求參數(shù)
          ?????*?@return
          ?????*?@author?daleyzou
          ?????*/
          ????@PostMapping("/test/{testId}")
          ????@NoRepeatSubmit(location?=?"thisIsTestLocation",?seconds?=?6)
          ????public?RsVo?thisIsTestLocation(@PathVariable?Integer?testId,?@RequestBody?RequestVo?requestVo)?throws?Throwable?{
          ????????//?睡眠?5?秒,模擬業(yè)務(wù)邏輯
          ????????Thread.sleep(5);
          ????????return?RsVo.success("test?is?return?success");
          ????}

          2、根據(jù)接口收到的 RequestBody 中指定變量名的值判斷唯一

          /**
          ?????*??根據(jù)請求參數(shù)里的?RequestBody?里獲取指定名稱的變量param5的值進行接口級別防重復(fù)點擊
          ?????*
          ?????*?@param?testId?測試id
          ?????*?@param?requestVo?請求參數(shù)
          ?????*?@return
          ?????*?@author?daleyzou
          ?????*/
          ????@PostMapping("/test/{testId}")
          ????@NoRepeatSubmit(location?=?"thisIsTestBody",?seconds?=?6,?argIndex?=?1,?name?=?"param5")
          ????public?RsVo?thisIsTestBody(@PathVariable?Integer?testId,?@RequestBody?RequestVo?requestVo)?throws?Throwable?{
          ????????//?睡眠?5?秒,模擬業(yè)務(wù)邏輯
          ????????Thread.sleep(5);
          ????????return?RsVo.success("test?is?return?success");
          ????}

          ps: jedis 2.9 和 springboot有各種兼容問題,無奈只有降低springboot的版本了

          運行結(jié)果

          收到響應(yīng):{"succeeded":true,"code":500,"msg":"操作過于頻繁,請稍后重試","data":null}
          收到響應(yīng):{"succeeded":true,"code":500,"msg":"操作過于頻繁,請稍后重試","data":null}
          收到響應(yīng):{"succeeded":true,"code":500,"msg":"操作過于頻繁,請稍后重試","data":null}
          收到響應(yīng):{"succeeded":true,"code":200,"msg":"success","data":"test?is?return?success"}

          測試用例

          package?com.dalelyzou.preventrepeatsubmit.controller;

          import?com.dalelyzou.preventrepeatsubmit.PreventrepeatsubmitApplicationTests;
          import?com.dalelyzou.preventrepeatsubmit.service.AsyncFeginService;
          import?com.dalelyzou.preventrepeatsubmit.vo.RequestVo;
          import?org.junit.jupiter.api.Test;
          import?org.springframework.beans.factory.annotation.Autowired;

          import?java.io.IOException;
          import?java.util.concurrent.ExecutorService;
          import?java.util.concurrent.Executors;

          /**
          ?*?TestControllerTest
          ?*?@description?防重復(fù)點擊測試類
          ?*?@author?daleyzou
          ?*?@date?2020年09月28日?17:13
          ?*?@version?1.3.1
          ?*/
          class?TestControllerTest?extends?PreventrepeatsubmitApplicationTests?{
          ????@Autowired
          ????AsyncFeginService?asyncFeginService;

          ????@Test
          ????public?void?thisIsTestLocation()?throws?IOException?{
          ????????RequestVo?requestVo?=?new?RequestVo();
          ????????requestVo.setParam5("random");
          ????????ExecutorService?executorService?=?Executors.newFixedThreadPool(4);
          ????????for?(int?i?=?0;?i?<=?3;?i++)?{
          ????????????executorService.execute(()?->?{
          ????????????????String?kl?=?asyncFeginService.thisIsTestLocation(requestVo);
          ????????????????System.err.println("收到響應(yīng):"?+?kl);
          ????????????});
          ????????}
          ????????System.in.read();
          ????}

          ????@Test
          ????public?void?thisIsTestBody()?throws?IOException?{
          ????????RequestVo?requestVo?=?new?RequestVo();
          ????????requestVo.setParam5("special");
          ????????ExecutorService?executorService?=?Executors.newFixedThreadPool(4);
          ????????for?(int?i?=?0;?i?<=?3;?i++)?{
          ????????????executorService.execute(()?->?{
          ????????????????String?kl?=?asyncFeginService.thisIsTestBody(requestVo);
          ????????????????System.err.println("收到響應(yīng):"?+?kl);
          ????????????});
          ????????}
          ????????System.in.read();
          ????}
          }

          定義一個注解

          package?com.dalelyzou.preventrepeatsubmit.aspect;

          import?java.lang.annotation.ElementType;
          import?java.lang.annotation.Retention;
          import?java.lang.annotation.RetentionPolicy;
          import?java.lang.annotation.Target;

          /**
          ?*?NoRepeatSubmit
          ?*?@description?重復(fù)點擊的切面
          ?*?@author?daleyzou
          ?*?@date?2020年09月23日?14:35
          ?*?@version?1.4.8
          ?*/
          @Target(ElementType.METHOD)
          @Retention(RetentionPolicy.RUNTIME)
          public?@interface?NoRepeatSubmit?{
          ????/**
          ?????*?鎖過期的時間
          ?????*?*/
          ????int?seconds()?default?5;
          ????/**
          ?????*?鎖的位置
          ?????*?*/
          ????String?location()?default?"NoRepeatSubmit";
          ????/**
          ?????*?要掃描的參數(shù)位置
          ?????*?*/
          ????int?argIndex()?default?0;
          ????/**
          ?????*?參數(shù)名稱
          ?????*?*/
          ????String?name()?default?"";
          }

          根據(jù)指定的注解定義一個切面,根據(jù)參數(shù)中的指定值來判斷請求是否重復(fù)

          package?com.dalelyzou.preventrepeatsubmit.aspect;

          import?com.dalelyzou.preventrepeatsubmit.constant.RedisKey;
          import?com.dalelyzou.preventrepeatsubmit.service.LockService;
          import?com.dalelyzou.preventrepeatsubmit.vo.RsVo;
          import?com.google.common.collect.Maps;
          import?com.google.gson.Gson;
          import?org.aspectj.lang.ProceedingJoinPoint;
          import?org.aspectj.lang.annotation.Around;
          import?org.aspectj.lang.annotation.Aspect;
          import?org.aspectj.lang.annotation.Pointcut;
          import?org.slf4j.Logger;
          import?org.slf4j.LoggerFactory;
          import?org.springframework.beans.factory.annotation.Autowired;
          import?org.springframework.stereotype.Component;
          import?org.springframework.util.StringUtils;

          import?java.lang.reflect.Field;
          import?java.util.Map;

          @Aspect
          @Component
          public?class?NoRepeatSubmitAspect?{
          ????private?static?final?Logger?logger?=?LoggerFactory.getLogger(NoRepeatSubmitAspect.class);

          ????private?static?Gson?gson?=?new?Gson();

          ????private?static?final?String?SUFFIX?=?"SUFFIX";

          ????@Autowired
          ????LockService?lockService;

          ????/**
          ?????*?橫切點
          ?????*/
          ????@Pointcut("@annotation(noRepeatSubmit)")
          ????public?void?repeatPoint(NoRepeatSubmit?noRepeatSubmit)?{
          ????}

          ????/**
          ?????*??接收請求,并記錄數(shù)據(jù)
          ?????*/
          ????@Around(value?=?"repeatPoint(noRepeatSubmit)")
          ????public?Object?doBefore(ProceedingJoinPoint?joinPoint,?NoRepeatSubmit?noRepeatSubmit)?{
          ????????String?key?=?RedisKey.NO_REPEAT_LOCK_PREFIX?+?noRepeatSubmit.location();
          ????????Object[]?args?=?joinPoint.getArgs();
          ????????String?name?=?noRepeatSubmit.name();
          ????????int?argIndex?=?noRepeatSubmit.argIndex();
          ????????String?suffix;
          ????????if?(StringUtils.isEmpty(name))?{
          ????????????suffix?=?String.valueOf(args[argIndex]);
          ????????}?else?{
          ????????????Map?keyAndValue?=?getKeyAndValue(args[argIndex]);
          ????????????Object?valueObj?=?keyAndValue.get(name);
          ????????????if?(valueObj?==?null)?{
          ????????????????suffix?=?SUFFIX;
          ????????????}?else?{
          ????????????????suffix?=?String.valueOf(valueObj);
          ????????????}
          ????????}
          ????????key?=?key?+?":"?+?suffix;
          ????????logger.info("==================================================");
          ????????for?(Object?arg?:?args)?{
          ????????????logger.info(gson.toJson(arg));
          ????????}
          ????????logger.info("==================================================");
          ????????int?seconds?=?noRepeatSubmit.seconds();
          ????????logger.info("lock?key?:?"?+?key);
          ????????if?(!lockService.isLock(key,?seconds))?{
          ????????????return?RsVo.fail("操作過于頻繁,請稍后重試");
          ????????}
          ????????try?{
          ????????????Object?proceed?=?joinPoint.proceed();
          ????????????return?proceed;
          ????????}?catch?(Throwable?throwable)?{
          ????????????logger.error("運行業(yè)務(wù)代碼出錯",?throwable);
          ????????????throw?new?RuntimeException(throwable.getMessage());
          ????????}?finally?{
          ????????????lockService.unLock(key);
          ????????}
          ????}

          ????public?static?Map?getKeyAndValue(Object?obj)?{
          ????????Map?map?=?Maps.newHashMap();
          ????????//?得到類對象
          ????????Class?userCla?=?(Class)?obj.getClass();
          ????????/*?得到類中的所有屬性集合?*/
          ????????Field[]?fs?=?userCla.getDeclaredFields();
          ????????for?(int?i?=?0;?i?????????????Field?f?=?fs[i];
          ????????????//?設(shè)置些屬性是可以訪問的
          ????????????f.setAccessible(true);
          ????????????Object?val?=?new?Object();
          ????????????try?{
          ????????????????val?=?f.get(obj);
          ????????????????//?得到此屬性的值
          ????????????????//?設(shè)置鍵值
          ????????????????map.put(f.getName(),?val);
          ????????????}?catch?(IllegalArgumentException?e)?{
          ????????????????logger.error("getKeyAndValue?IllegalArgumentException",?e);
          ????????????}?catch?(IllegalAccessException?e)?{
          ????????????????logger.error("getKeyAndValue?IllegalAccessException",?e);
          ????????????}

          ????????}
          ????????logger.info("掃描結(jié)果:"?+?gson.toJson(map));
          ????????return?map;
          ????}
          }





          粉絲福利:108本java從入門到大神精選電子書領(lǐng)取

          ???

          ?長按上方鋒哥微信二維碼?2 秒
          備注「1234」即可獲取資料以及
          可以進入java1234官方微信群



          感謝點贊支持下哈?

          瀏覽 46
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  中文字幕日本无码 | 91天堂网 | 一级爱爱免费视频 | 久久久久国际精品视频 | 99热男人 |