這篇文章主要介紹“如何使用自定義注解+redis的攔截器實(shí)現(xiàn)冪等性校驗(yàn)”,在日常操作中,相信很多人在如何使用自定義注解+Redis的攔截器實(shí)現(xiàn)冪等性校驗(yàn)問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”如何使用自定義注解+Redis的攔截器實(shí)現(xiàn)冪等性校驗(yàn)”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!
網(wǎng)站建設(shè)哪家好,找成都創(chuàng)新互聯(lián)!專注于網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、小程序開發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了順德免費(fèi)建站歡迎大家使用!
實(shí)現(xiàn)方法
編寫一個(gè)自定義注解和一個(gè)攔截器,本文中的自定義注解可以指定傳入超時(shí)時(shí)間(默認(rèn)是60秒),攔截器對使用注解的方法進(jìn)行攔截,獲取到傳入的參數(shù)和超時(shí)時(shí)間,將傳入的一個(gè)或多個(gè)參數(shù)拼接成一個(gè)json字符串,使用md5進(jìn)行加密后把它作為key存入Redis緩存中,如果根據(jù)key在超時(shí)時(shí)間范圍內(nèi)能找到相同的內(nèi)容,則返回表單內(nèi)容已提交提示,否則繼續(xù)執(zhí)行方法。
自定義一個(gè)@Idempotent注解
/** * 自定義防重復(fù)提交注解 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Idempotent { /**可以傳入指定的重復(fù)提交限定時(shí)間,默認(rèn)60秒*/ long value() default 60000; }
自定義一個(gè)IdempotentAspect攔截器
/** * 攔截器 * @author 豆芽 * @Date 2020-11-11 15:05 */ @Aspect @Component public class IdempotentAspect { private Logger logger = LoggerFactory.getLogger(IdempotentAspect.class); @Autowired private RedisService redisService; @Around("@annotation(idempotent)") public Object aroundMethod(ProceedingJoinPoint pjp,Idempotent idempotent)throws Throwable{ /**獲取執(zhí)行方法的參數(shù)*/ Object[] args = pjp.getArgs(); /**獲取注解傳入的超時(shí)時(shí)間*/ long timeOut = idempotent.value(); /**使用MD5對傳入的參數(shù)進(jìn)行加密*/ String encode = getMd5Value(args); try{ /** 校驗(yàn)是否重復(fù)提交過,如果沒有,則按指定超時(shí)時(shí)間存入Redis緩存 */ boolean checkFormToken = redisService.checkForm(encode,timeOut); if (checkFormToken) { /**這是一個(gè)自定義的異常類,可以自己編寫*/ throw new CommonException(Code.RepeatSubmit,"表單內(nèi)容已經(jīng)提交"); } /**繼續(xù)執(zhí)行方法*/ return pjp.proceed(); } catch (CommonException e) { logger.error("運(yùn)行時(shí)錯誤:" + e.getMessage(), e); if (Code.RepeatSubmit.getCode() != e.getCode()) { /**調(diào)用方法可能會存在其他的CommonException自定義異常,需要刪除校驗(yàn)的key,支持重復(fù)提交*/ redisService.delete(encode); } throw e; } catch (Exception e){ logger.error("冪等性校驗(yàn)出錯:" + e.getMessage(), e); throw e; } } /** * 使用MD5對傳入的參數(shù)進(jìn)行加密 * @param args * @return */ private String getMd5Value(Object[] args) { String md5 = "null"; if (args.length == 0) { return md5; } else { StringBuilder jsonString = new StringBuilder(JSON.toJSONString(args)); /**使用md5工具類對字符串進(jìn)行加密*/ md5 = SecureUtil.md5Encode(jsonString.toString()); } return md5; } }
封裝的RedisService類
@Component public class RedisService { private Logger logger = LoggerFactory.getLogger(RedisService.class); @Autowired private StringRedisTemplate stringRedisTemplate; /** * 將對象存入緩存中 * @param key key * @param obj 對象數(shù)據(jù) * @param timeout 超時(shí)時(shí)間 */ public void set(String key, Object obj, long timeout) { if (obj instanceof String) { stringRedisTemplate.opsForValue().set(key, (String) obj, timeout, TimeUnit.MILLISECONDS); return; } String json = JSON.toJSONString(obj); stringRedisTemplate.opsForValue().set(key, json, timeout, TimeUnit.MILLISECONDS); } /** * 根據(jù)Key查詢緩存中的數(shù)據(jù) * @param key * @return */ public String get(final String key) { if (StringUtils.isEmpty(key)) { logger.warn("獲取Redis緩存,傳入的Key為空"); return null; } return stringRedisTemplate.opsForValue().get(key); } /** * 根據(jù)key刪除緩存數(shù)據(jù) * @param key */ public void delete(String key) { stringRedisTemplate.delete(key); } /** * 查詢緩存是否存在 * @param checkCase * @return */ public boolean checkForm(String checkCase,long timeOut){ String cacheValue = get(checkCase); /**如果查詢緩存不為空,返回true*/ if (StringUtils.isNotEmpty(cacheValue)){ return true; } /**否則將對象存入緩存中,IdUtil.randomUUID()為hutool的UUID生成工具類,可到hutool官網(wǎng)加載相關(guān)依賴*/ set(checkCase, IdUtil.randomUUID(), timeOut); return false; } }
自定義的錯誤碼枚舉Code
public enum Code { ErrorSystem(500,"系統(tǒng)繁忙!"), RepeatSubmit(4,"重復(fù)提交") ; private int code; private String msg; Code(int code, String msg) { this.code = code; this.msg = msg; } public int getCode() { return code; } public String getMsg() { return msg; } }
自定義異常類CommonException
public class CommonException extends RuntimeException { /** 錯誤碼 */ private int code = Code.ErrorSystem.getCode(); /** 錯誤信息 */ private String msg = Code.ErrorSystem.getMsg(); public CommonException(Code code) { super("[" + code.getCode() + ":" + code.toString() +"]" + code.getMsg()); this.code = code.getCode(); this.msg = code.getMsg(); } public CommonException(String msg) { super("[" + Code.ErrorSystem.getCode() + ":" + Code.ErrorSystem.toString() +"]" + msg); this.code = Code.ErrorSystem.getCode(); this.msg = msg; } public CommonException(Code code, String msg) { super("[" + code.getCode() + ":" + code.toString() +"]" + msg); this.code = code.getCode(); this.msg = msg; } public int getCode() { return code; } public String getMsg() { return msg; } }
測試類RedisController,@Idempotent注解可以直接value參數(shù),這個(gè)參數(shù)可以設(shè)置本次請求的參數(shù)在redis中的存活時(shí)間,不傳參默認(rèn)存活60秒
/** * @author 豆芽 * @Date 2020-11-11 11:09 */ @RestController public class RedisController { private Logger logger = LoggerFactory.getLogger(RedisController.class); /** * 自定義注解+Redis+MD5加密的攔截器實(shí)現(xiàn)冪等性校驗(yàn) * @param name * @param age * @return */ @Idempotent @RequestMapping(value = "/auth/redis/idempotent",method = {RequestMethod.GET, RequestMethod.POST}) public String idempotentTest(String name,String age){ logger.info("name: "+name); logger.info("age: "+age); // TODO 執(zhí)行保存或更新方法 return "執(zhí)行成功"; } }
第一次發(fā)起請求返回執(zhí)行成功
緊接著馬上再發(fā)起一次相同參數(shù)的請求,系統(tǒng)會拋出“表單內(nèi)容已經(jīng)提交”的異常
到此,關(guān)于“如何使用自定義注解+Redis的攔截器實(shí)現(xiàn)冪等性校驗(yàn)”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!