国产成人精品亚洲777人妖,欧美日韩精品一区视频,最新亚洲国产,国产乱码精品一区二区亚洲

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

springboot實(shí)現(xiàn)防重復(fù)提交和防重復(fù)點(diǎn)擊的示例

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

背景

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

目標(biāo)

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

說(shuō)明

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

技術(shù)方案

springboot + redis鎖 + 注解

使用 feign client 進(jìn)行請(qǐng)求測(cè)試

最終的使用實(shí)例

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

/** * 根據(jù)請(qǐng)求參數(shù)里的 PathVariable 里獲取的變量進(jìn)行接口級(jí)別防重復(fù)點(diǎn)擊 * * @param testId 測(cè)試id * @param requestVo 請(qǐng)求參數(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ù)請(qǐng)求參數(shù)里的 RequestBody 里獲取指定名稱的變量param5的值進(jìn)行接口級(jí)別防重復(fù)點(diǎn)擊 * * @param testId 測(cè)試id * @param requestVo 請(qǐng)求參數(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有各種兼容問(wèn)題,無(wú)奈只有降低springboot的版本了

運(yùn)行結(jié)果

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

測(cè)試用例

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ù)點(diǎn)擊測(cè)試類 * @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(); }}

定義一個(gè)注解

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ù)點(diǎn)擊的切面 * @author daleyzou * @date 2020年09月23日 14:35 * @version 1.4.8 */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface NoRepeatSubmit { /** * 鎖過(guò)期的時(shí)間 * */ int seconds() default 5; /** * 鎖的位置 * */ String location() default 'NoRepeatSubmit'; /** * 要掃描的參數(shù)位置 * */ int argIndex() default 0; /** * 參數(shù)名稱 * */ String name() default '';}

根據(jù)指定的注解定義一個(gè)切面,根據(jù)參數(shù)中的指定值來(lái)判斷請(qǐng)求是否重復(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@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; /** * 橫切點(diǎn) */ @Pointcut('@annotation(noRepeatSubmit)') public void repeatPoint(NoRepeatSubmit noRepeatSubmit) { } /** * 接收請(qǐng)求,并記錄數(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<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('操作過(guò)于頻繁,請(qǐng)稍后重試'); } try { Object proceed = joinPoint.proceed(); return proceed; } catch (Throwable throwable) { logger.error('運(yùn)行業(yè)務(wù)代碼出錯(cuò)', throwable); throw new RuntimeException(throwable.getMessage()); } finally { lockService.unLock(key); } } public static Map<String, Object> getKeyAndValue(Object obj) { Map<String, Object> map = Maps.newHashMap(); // 得到類對(duì)象 Class userCla = (Class) obj.getClass(); /* 得到類中的所有屬性集合 */ Field[] fs = userCla.getDeclaredFields(); for (int i = 0; i < fs.length; i++) { Field f = fs[i]; // 設(shè)置些屬性是可以訪問(wèn)的 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; }}

項(xiàng)目完整代碼

https://github.com/daleyzou/PreventRepeatSubmit

以上就是springboot實(shí)現(xiàn)防重復(fù)提交和防重復(fù)點(diǎn)擊的示例的詳細(xì)內(nèi)容,更多關(guān)于springboot實(shí)現(xiàn)防重復(fù)提交和防重復(fù)點(diǎn)擊的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Spring
相關(guān)文章:
主站蜘蛛池模板: 门源| 同德县| 浦江县| 永嘉县| 丰都县| 呈贡县| 乐昌市| 襄樊市| 岳阳市| 玉山县| 新乐市| 南投市| 安丘市| 周口市| 涞源县| 怀宁县| 蓬安县| 河东区| 西华县| 赫章县| 舞钢市| 确山县| 达州市| 兴文县| 甘肃省| 石屏县| 高雄县| 永善县| 焦作市| 邹平县| 乌海市| 彩票| 镇康县| 郁南县| 农安县| 凤冈县| 衡东县| 临泽县| 泰顺县| 舒城县| 小金县|