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

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

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

瀏覽:5日期: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香蕉成人 | 国产成人精品午夜免费 | 欧美一级乱理片免费观看 | 成人网18免费网 | 国产精品v在线播放观看 | 免费欧美一级片 | 亚洲毛片在线观看 | 久久亚洲一级毛片 | 欧美一级网站 | 国内精品a| 一级毛片视频 | 深夜福利视频在线看免费 | 美美女高清毛片视频免费观看 | 成人亚洲综合 | 99在线观看免费视频 | 亚洲午夜一区二区三区 | 国产成人免费高清视频 | 国产美女操| 亚洲精品在线看 | 亚洲制服欧美自拍另类 | 在线观看视频中文字幕 | 九九九国产在线 | 99pao在线视频成精品 | 久久视频精品线视频在线网站 | 日本视频在线免费播放 | 国产一区二区三区不卡在线观看 | 九九精品在线观看 | 涩涩网站| 日本a级毛片视频播放 | 亚洲欧美一区二区三区在线播放 | 欧美日产国产亚洲综合图区一 | 国产人做人爱免费视频 | 午夜性爽视频男人的天堂在线 | 国产日本三级欧美三级妇三级四 | 日本一区二区三区精品视频 | 成人黄色在线免费观看 | 久久国内精品自在自线观看 |