国产成人精品久久免费动漫-国产成人精品天堂-国产成人精品区在线观看-国产成人精品日本-a级毛片无码免费真人-a级毛片毛片免费观看久潮喷

您的位置:首頁技術文章
文章詳情頁

springboot實現防重復提交和防重復點擊的示例

瀏覽:2日期:2023-04-20 10:13:08

背景

同一條數據被用戶點擊了多次,導致數據冗余,需要防止弱網絡等環境下的重復點擊

目標

通過在指定的接口處添加注解,實現根據指定的接口參數來防重復點擊

說明

這里的重復點擊是指在指定的時間段內多次點擊按鈕

技術方案

springboot + redis鎖 + 注解

使用 feign client 進行請求測試

最終的使用實例

1、根據接口收到 PathVariable 參數判斷唯一

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

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

/** * 根據請求參數里的 RequestBody 里獲取指定名稱的變量param5的值進行接口級別防重復點擊 * * @param testId 測試id * @param requestVo 請求參數 * @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 秒,模擬業務邏輯 Thread.sleep(5); return RsVo.success('test is return success'); }

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

運行結果

收到響應:{'succeeded':true,'code':500,'msg':'操作過于頻繁,請稍后重試','data':null}收到響應:{'succeeded':true,'code':500,'msg':'操作過于頻繁,請稍后重試','data':null}收到響應:{'succeeded':true,'code':500,'msg':'操作過于頻繁,請稍后重試','data':null}收到響應:{'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 防重復點擊測試類 * @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('收到響應:' + 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('收到響應:' + 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 重復點擊的切面 * @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'; /** * 要掃描的參數位置 * */ int argIndex() default 0; /** * 參數名稱 * */ String name() default '';}

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

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@Componentpublic 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) { } /** * 接收請求,并記錄數據 */ @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<String, Object> 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('運行業務代碼出錯', throwable); throw new RuntimeException(throwable.getMessage()); } finally { lockService.unLock(key); } } public static Map<String, Object> getKeyAndValue(Object obj) { Map<String, Object> map = Maps.newHashMap(); // 得到類對象 Class userCla = (Class) obj.getClass(); /* 得到類中的所有屬性集合 */ Field[] fs = userCla.getDeclaredFields(); for (int i = 0; i < fs.length; i++) { Field f = fs[i]; // 設置些屬性是可以訪問的 f.setAccessible(true); Object val = new Object(); try { val = f.get(obj); // 得到此屬性的值 // 設置鍵值 map.put(f.getName(), val); } catch (IllegalArgumentException e) { logger.error('getKeyAndValue IllegalArgumentException', e); } catch (IllegalAccessException e) { logger.error('getKeyAndValue IllegalAccessException', e); } } logger.info('掃描結果:' + gson.toJson(map)); return map; }}

項目完整代碼

https://github.com/daleyzou/PreventRepeatSubmit

以上就是springboot實現防重復提交和防重復點擊的示例的詳細內容,更多關于springboot實現防重復提交和防重復點擊的資料請關注好吧啦網其它相關文章!

標簽: Spring
相關文章:
主站蜘蛛池模板: 久久久久久91精品色婷婷 | 91精品国产薄丝高跟在线看 | 亚洲tv成人天堂在线播放 | 一级毛片aaaaaa视频免费看 | 2022国内精品免费福利视频 | 国产成人精品亚洲日本在线观看 | 一本久久道久久爱 | 国产乱子精品免费视观看片 | 性欧美另类老妇高清 | 久久久久久国产精品免费免 | a级黄色毛片免费播放视频 a级精品九九九大片免费看 | 亚洲欧美日本国产 | 免费高清欧美一区二区视频 | 真人毛片视频 | 国产自产v一区二区三区c | 在线观看视频一区二区三区 | 精品日韩欧美一区二区三区在线播放 | 国内在线播放 | 作爱视频在线免费观看 | 国产不卡在线观看视频 | 久久久久久久久久免费视频 | 午夜在线视频一区二区三区 | 久久久精品免费热线观看 | 欧美无极品 | 手机看片1024欧美日韩你懂的 | 国产精品一区二区三区四区五区 | 日韩一区二区视频在线观看 | 国产亚洲欧美另类久久久 | 国产一级做a爱片久久毛片a | 精品久久久久中文字幕日本 | 国产成人精品亚洲日本语音 | 欧美日韩色黄大片在线视频 | 国产成人一区二区三区精品久久 | mm在线精品视频 | 色手机在线 | 免费一级特黄a | 国产高清视频在线观看 | 全部在线美女网站免费观看 | 国产精品久久九九 | 欧美很黄视频在线观看 | 91热久久免费频精品黑人99 |