Springboot怎么利用Redis實現接口冪等性攔截

蝸牛 互聯網技術資訊 2022-06-24 19 0

今天小編給大家分享一下Springboot怎么利用Redis實現接口冪等性攔截的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

正文

Springboot怎么利用Redis實現接口冪等性攔截  springboot 第1張

自定義注解 怎么玩的 : ? ? ?

①標記哪個接口需要進行冪等性攔截 ? ? ? ?

②每個接口可以要求冪等性范圍時間不一樣,舉例:可以2秒內,可以3秒內,時間自己傳 ? ? ? ?

③ 一旦觸發了,提示語可以不同 ,舉例:VIP的接口,普通用戶的接口,提示語不一樣(開玩笑)

效果:

Springboot怎么利用Redis實現接口冪等性攔截  springboot 第2張

Springboot怎么利用Redis實現接口冪等性攔截  springboot 第3張

實戰開始

核心三件套

注解、攔截器、攔截器配置

① RepeatDaMie.java

import?java.lang.annotation.ElementType;
import?java.lang.annotation.Retention;
import?java.lang.annotation.RetentionPolicy;
import?java.lang.annotation.Target;
?
/**
?*?@Author:?JCccc
?*?@Date:?2022-6-13?9:04
?*?@Description:??自定義注解,防止重復提交
?*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public?@interface?RepeatDaMie?{
?
????/**
?????*?時間ms限制
?????*/
????public?int?second()?default?1;
?
????/**
?????*?提示消息
?????*/
????public?String?describe()?default?"重復提交了,兄弟";
????
}

②ApiRepeatInterceptor.java

import?com.example.repeatdemo.annotation.RepeatDaMie;
import?com.example.repeatdemo.util.ContextUtil;
import?com.example.repeatdemo.util.Md5Encrypt;
import?com.example.repeatdemo.util.RedisUtils;
import?com.example.repeatdemo.wrapper.CustomHttpServletRequestWrapper;
import?com.fasterxml.jackson.databind.ObjectMapper;
import?org.slf4j.Logger;
import?org.slf4j.LoggerFactory;
import?org.springframework.stereotype.Component;
import?org.springframework.web.method.HandlerMethod;
import?org.springframework.web.servlet.HandlerInterceptor;
?
import?javax.servlet.http.HttpServletRequest;
import?javax.servlet.http.HttpServletResponse;
import?java.io.IOException;
import?java.util.Objects;
?
/**
?*?@Author:?JCccc
?*?@Date:?2022-6-15?9:11
?*?@Description:?接口冪等性校驗攔截器
?*/
@Component
public?class?ApiRepeatInterceptor?implements?HandlerInterceptor?{
?
????private?final?Logger?log?=?LoggerFactory.getLogger(this.getClass());
?
????private?static?final?String?POST="POST";
????private?static?final?String?GET="GET";
????@Override
????public?boolean?preHandle(HttpServletRequest?request,?HttpServletResponse?response,?Object?handler)?throws?Exception?{
????????try?{
????????????if?(handler?instanceof?HandlerMethod)?{
????????????????HandlerMethod?handlerMethod?=?(HandlerMethod)?handler;
????????????????//?獲取RepeatDaMie注解
????????????????RepeatDaMie?repeatDaMie?=?handlerMethod.getMethodAnnotation(RepeatDaMie.class);
????????????????if?(null==repeatDaMie)?{
????????????????????return?true;
????????????????}
????????????????//限制的時間范圍
????????????????int?seconds?=?repeatDaMie.second();
????????????????//這個用戶唯一標識,可以自己細微調整,是userId還是token還是sessionId還是不需要
????????????????String?userUniqueKey?=?request.getHeader("userUniqueKey");
????????????????String?method?=?request.getMethod();
????????????????String?apiParams?=?"";
????????????????if?(GET.equals(method)){
????????????????????log.info("GET請求來了");
????????????????????apiParams?=?new?ObjectMapper().writeValueAsString(request.getParameterMap());
????????????????}else?if?(POST.equals(method)){
????????????????????log.info("POST請求來了");
????????????????????CustomHttpServletRequestWrapper?wrapper?=?(CustomHttpServletRequestWrapper)?request;
????????????????????apiParams?=?wrapper.getBody();
????????????????}
???????????????log.info("當前參數是:{}",apiParams);
????????????????//?存儲key
????????????????String?keyRepeatDaMie?=?Md5Encrypt.md5(userUniqueKey+request.getServletPath()+apiParams)?;
????????????????RedisUtils?redisUtils?=?ContextUtil.getBean(RedisUtils.class);
????????????????if?(Objects.nonNull(redisUtils.get(keyRepeatDaMie))){
???????????????????log.info("重復請求了,重復請求了,攔截了");
???????????????????returnData(response,repeatDaMie.describe());
???????????????????return?false;
???????????????}else?{
????????????????????redisUtils.setWithTime(keyRepeatDaMie,?true,seconds);
???????????????}
?
????????????}
????????????return?true;
????????}?catch?(Exception?e)?{
????????????log.warn("請求過于頻繁請稍后再試");
????????????e.printStackTrace();
????????}
????????return?true;
????}
?
????public?void?returnData(HttpServletResponse?response,String?msg)?throws?IOException?{
????????response.setCharacterEncoding("UTF-8");
????????response.setContentType("application/json;?charset=utf-8");
????????ObjectMapper?objectMapper?=?new?ObjectMapper();
????????//這里傳提示語可以改成自己項目的返回數據封裝的類
????????response.getWriter().println(objectMapper.writeValueAsString(msg));
????????return;
????}
?
}

③ WebConfig.java

import?org.springframework.context.annotation.Configuration;
import?org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import?org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
?
/**
?*?@Author:?JCccc
?*?@Date:?2022-6-15?9:24
?*?@Description:
?*/
@Configuration
public?class?WebConfig?implements?WebMvcConfigurer?{
?
????
????@Override
????public?void?addInterceptors(InterceptorRegistry?registry)?{
????????registry.addInterceptor(new?ApiRepeatInterceptor()).addPathPatterns("/**");
????}
}

工具類三件套

①ContextUtil.java

import?org.springframework.beans.BeansException;
import?org.springframework.context.ApplicationContext;
import?org.springframework.context.ApplicationContextAware;
import?org.springframework.stereotype.Component;
/**
?*?@Author:?JCccc
?*?@Date:?2022-6-15?9:24
?*?@Description:
?*/
@Component
public?final?class?ContextUtil?implements?ApplicationContextAware?{
????protected?static?ApplicationContext?applicationContext?;
????@Override
????public?void?setApplicationContext(ApplicationContext?arg0)?throws?BeansException?{
????????if?(applicationContext?==?null)?{
????????????applicationContext?=?arg0;
????????}
????}
????public?static?Object?getBean(String?name)?{
????????//name表示其他要注入的注解name名
????????return?applicationContext.getBean(name);
????}
????/**
?????*?拿到ApplicationContext對象實例后就可以手動獲取Bean的注入實例對象
?????*/
????public?static?<T>?T?getBean(Class<T>?clazz)?{
????????return?applicationContext.getBean(clazz);
????}
}

②Md5Encrypt.java

import?java.io.UnsupportedEncodingException;
import?java.security.MessageDigest;
import?java.security.NoSuchAlgorithmException;
?
/**
?*?@Author:?JCccc
?*?@CreateTime:?2018-10-30
?*?@Description:
?*/
public?class?Md5Encrypt?{
?
????private?static?final?char[]?DIGITS?=?{'0',?'1',?'2',?'3',?'4',?'5',?'6',?'7',?'8',?'9',?'a',
????????????'b',?'c',?'d',?'e',?'f'};
?
????/**
?????*?對字符串進行MD5加密
?????*
?????*?@param?text?明文
?????*?@return?密文
?????*/
????public?static?String?md5(String?text)?{
????????MessageDigest?msgDigest?=?null;
?
????????try?{
????????????msgDigest?=?MessageDigest.getInstance("MD5");
????????}?catch?(NoSuchAlgorithmException?e)?{
????????????throw?new?IllegalStateException("System?doesn't?support?MD5?algorithm.");
????????}
?
????????try?{
????????????//?注意該接口是按照指定編碼形式簽名
????????????msgDigest.update(text.getBytes("UTF-8"));
?
????????}?catch?(UnsupportedEncodingException?e)?{
?
????????????throw?new?IllegalStateException("System?doesn't?support?your??EncodingException.");
?
????????}
?
????????byte[]?bytes?=?msgDigest.digest();
?
????????String?md5Str?=?new?String(encodeHex(bytes));
?
????????return?md5Str;
????}
?
????private?static?char[]?encodeHex(byte[]?data)?{
?
????????int?l?=?data.length;
?
????????char[]?out?=?new?char[l?<<?1];
????????//?two?characters?form?the?hex?value.
????????for?(int?i?=?0,?j?=?0;?i?<?l;?i++)?{
????????????out[j++]?=?DIGITS[(0xF0?&?data[i])?>>>?4];
????????????out[j++]?=?DIGITS[0x0F?&?data[i]];
????????}
?
????????return?out;
????}
}

③RedisUtils.java

import?org.springframework.beans.factory.annotation.Autowired;
import?org.springframework.data.redis.core.*;
import?org.springframework.stereotype.Component;
import?java.io.Serializable;
import?java.util.List;
import?java.util.Set;
import?java.util.concurrent.TimeUnit;
?
?
@Component
public?class?RedisUtils?{
?
????@Autowired
????private?RedisTemplate?redisTemplate;
?
????/**
?????*?寫入String型?[?鍵,值]
?????*
?????*?@param?key
?????*?@param?value
?????*?@return
?????*/
?
????public?boolean?set(final?String?key,?Object?value)?{
????????boolean?result?=?false;
????????try?{
????????????ValueOperations<Serializable,?Object>?operations?=?redisTemplate.opsForValue();
????????????operations.set(key,?value);
????????????result?=?true;
????????}?catch?(Exception?e)?{
????????????e.printStackTrace();
????????}
????????return?result;
?
????}
?
?
?
????/**
?????*?寫入String型,順便帶有過期時間?[?鍵,值]
?????*
?????*?@param?key
?????*?@param?value
?????*?@return
?????*/
?
????public?boolean?setWithTime(final?String?key,?Object?value,int?seconds)?{
????????boolean?result?=?false;
????????try?{
?
????????????ValueOperations<Serializable,?Object>?operations?=?redisTemplate.opsForValue();
????????????operations.set(key,?value,seconds,?TimeUnit.SECONDS);
????????????result?=?true;
????????}?catch?(Exception?e)?{
????????????e.printStackTrace();
????????}
????????return?result;
?
????}
?
?
?
????/**
?????*?批量刪除對應的value
?????*
?????*?@param?keys
?????*/
?
????public?void?remove(final?String...?keys)?{
????????for?(String?key?:?keys)?{
????????????remove(key);
????????}
????}
?
????/**
?????*?批量刪除key
?????*
?????*?@param?pattern
?????*/
?
????public?void?removePattern(final?String?pattern)?{
????????Set<Serializable>?keys?=?redisTemplate.keys(pattern);
????????if?(keys.size()?>?0)
????????????redisTemplate.delete(keys);
????}
?
????/**
?????*?刪除對應的value
?????*
?????*?@param?key
?????*/
?
????public?void?remove(final?String?key)?{
????????if?(exists(key))?{
????????????redisTemplate.delete(key);
????????}
????}
?
?
????/**
?????*?判斷緩存中是否有對應的value
?????*
?????*?@param?key
?????*?@return
?????*/
?
????public?boolean?exists(final?String?key)?{
????????return?redisTemplate.hasKey(key);
????}
?
?
????/**
?????*?讀取緩存
?????*
?????*?@param?key
?????*?@return
?????*/
?
????public?Object?get(final?String?key)?{
????????Object?result?=?null;
????????ValueOperations<Serializable,?Object>?operations?=?redisTemplate.opsForValue();
????????result?=?operations.get(key);
????????return?result;
????}
?
?
????/**
?????*?哈希?添加
?????*?hash?一個鍵值(key->value)對集合
?????*
?????*?@param?key
?????*?@param?hashKey
?????*?@param?value
?????*/
?
????public?void?hmSet(String?key,?Object?hashKey,?Object?value)?{
?
????????HashOperations<String,?Object,?Object>?hash?=?redisTemplate.opsForHash();
?
????????hash.put(key,?hashKey,?value);
?
????}
?
?
????/**
?????*?Hash獲取數據
?????*
?????*?@param?key
?????*?@param?hashKey
?????*?@return
?????*/
?
????public?Object?hmGet(String?key,?Object?hashKey)?{
????????HashOperations<String,?Object,?Object>?hash?=?redisTemplate.opsForHash();
????????return?hash.get(key,?hashKey);
?
????}
?
?
????/**
?????*?列表添加
?????*?list:lpush?key?value1
?????*
?????*?@param?k
?????*?@param?v
?????*/
?
????public?void?lPush(String?k,?Object?v)?{
????????ListOperations<String,?Object>?list?=?redisTemplate.opsForList();
????????list.rightPush(k,?v);
????}
?
?
????/**
?????*?列表List獲取
?????*?lrange:?key?0?10?(讀取的個數?從0開始?讀取到下標為10?的數據)
?????*
?????*?@param?k
?????*?@param?l
?????*?@param?l1
?????*?@return
?????*/
?
????public?List<Object>?lRange(String?k,?long?l,?long?l1)?{
????????ListOperations<String,?Object>?list?=?redisTemplate.opsForList();
????????return?list.range(k,?l,?l1);
????}
?
?
????/**
?????*?Set集合添加
?????*
?????*?@param?key
?????*?@param?value
?????*/
?
????public?void?add(String?key,?Object?value)?{
????????SetOperations<String,?Object>?set?=?redisTemplate.opsForSet();
????????set.add(key,?value);
????}
?
?
????/**
?????*?Set?集合獲取
?????*
?????*?@param?key
?????*?@return
?????*/
?
????public?Set<Object>?setMembers(String?key)?{
?
????????SetOperations<String,?Object>?set?=?redisTemplate.opsForSet();
?
????????return?set.members(key);
?
????}
?
?
????/**
?????*?Sorted?set?:有序集合添加
?????*
?????*?@param?key
?????*?@param?value
?????*?@param?scoure
?????*/
?
????public?void?zAdd(String?key,?Object?value,?double?scoure)?{
????????ZSetOperations<String,?Object>?zset?=?redisTemplate.opsForZSet();
????????zset.add(key,?value,?scoure);
????}
?
?
????/**
?????*?Sorted?set:有序集合獲取
?????*
?????*?@param?key
?????*?@param?scoure
?????*?@param?scoure1
?????*?@return
?????*/
?
????public?Set<Object>?rangeByScore(String?key,?double?scoure,?double?scoure1)?{
?
????????ZSetOperations<String,?Object>?zset?=?redisTemplate.opsForZSet();
?
????????return?zset.rangeByScore(key,?scoure,?scoure1);
?
????}
?
?
????/**
?????*?根據key獲取Set中的所有值
?????*
?????*?@param?key?鍵
?????*?@return
?????*/
?
????public?Set<Integer>?sGet(String?key)?{
????????try?{
????????????return?redisTemplate.opsForSet().members(key);
????????}?catch?(Exception?e)?{
????????????e.printStackTrace();
????????????return?null;
????????}
????}
?
?
????/**
?????*?根據value從一個set中查詢,是否存在
?????*
?????*?@param?key???鍵
?????*?@param?value?值
?????*?@return?true?存在?false不存在
?????*/
?
????public?boolean?sHasKey(String?key,?Object?value)?{
????????try?{
????????????return?redisTemplate.opsForSet().isMember(key,?value);
????????}?catch?(Exception?e)?{
????????????e.printStackTrace();
????????????return?false;
????????}
????}
?
}

REDIS配置類

RedisConfig.java

import?com.fasterxml.jackson.annotation.JsonAutoDetect;
import?com.fasterxml.jackson.annotation.PropertyAccessor;
import?com.fasterxml.jackson.databind.ObjectMapper;
import?org.springframework.cache.CacheManager;
import?org.springframework.cache.annotation.EnableCaching;
import?org.springframework.context.annotation.Bean;
import?org.springframework.context.annotation.Configuration;
import?org.springframework.data.redis.cache.RedisCacheConfiguration;
import?org.springframework.data.redis.cache.RedisCacheManager;
import?org.springframework.data.redis.connection.RedisConnectionFactory;
import?org.springframework.data.redis.core.RedisTemplate;
import?org.springframework.data.redis.core.StringRedisTemplate;
import?org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import?org.springframework.data.redis.serializer.RedisSerializationContext;
import?org.springframework.data.redis.serializer.StringRedisSerializer;
import?static?org.springframework.data.redis.cache.RedisCacheConfiguration.defaultCacheConfig;
?
/**
?*?@Author:?JCccc
?*?@CreateTime:?2018-09-11
?*?@Description:
?*/
@Configuration
@EnableCaching
public?class?RedisConfig?{
????@Bean
????public?CacheManager?cacheManager(RedisConnectionFactory?connectionFactory)?{
????????RedisCacheConfiguration?cacheConfiguration?=
????????????????defaultCacheConfig()
????????????????????????.disableCachingNullValues()
????????????????????????.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new?Jackson2JsonRedisSerializer(Object.class)));
????????return?RedisCacheManager.builder(connectionFactory).cacheDefaults(cacheConfiguration).build();
????}
?
?
????@Bean
????public?RedisTemplate<String,?Object>?redisTemplate(RedisConnectionFactory?factory)?{
????????RedisTemplate<String,?Object>?redisTemplate?=?new?RedisTemplate<>();
????????redisTemplate.setConnectionFactory(factory);
????????Jackson2JsonRedisSerializer?jackson2JsonRedisSerializer?=?new?Jackson2JsonRedisSerializer(Object.class);
????????ObjectMapper?om?=?new?ObjectMapper();
????????om.setVisibility(PropertyAccessor.ALL,?JsonAutoDetect.Visibility.ANY);
????????om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
????????jackson2JsonRedisSerializer.setObjectMapper(om);
????????//序列化設置?,這樣為了存儲操作對象時正常顯示的數據,也能正常存儲和獲取
????????redisTemplate.setKeySerializer(new?StringRedisSerializer());
????????redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
????????redisTemplate.setHashKeySerializer(new?StringRedisSerializer());
????????redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
????????return?redisTemplate;
????}
????@Bean
????public?StringRedisTemplate?stringRedisTemplate(RedisConnectionFactory?factory)?{
????????StringRedisTemplate?stringRedisTemplate?=?new?StringRedisTemplate();
????????stringRedisTemplate.setConnectionFactory(factory);
????????return?stringRedisTemplate;
????}
?
}

最后寫測試接口,看看效果(一個POST,一個GET):

故意把時間放大,1000秒內重復調用,符合我們攔截規則的都會被攔截。

TestController.java

import?com.example.repeatdemo.dto.PayOrderApply;
import?com.example.repeatdemo.annotation.RepeatDaMie;
import?org.slf4j.Logger;
import?org.slf4j.LoggerFactory;
import?org.springframework.web.bind.annotation.*;
?
/**
?*?@Author:?JCccc
?*?@Date:?2022-6-05?9:44
?*?@Description:
?*/
@RestController
public?class?TestController?{
????private?final?Logger?log?=?LoggerFactory.getLogger(this.getClass());
?
????@RepeatDaMie(second?=?1000,describe?=?"尊敬的客戶,您慢點")
????@PostMapping(value?=?"/doPost")
????@ResponseBody
????public?void?test(@RequestBody?PayOrderApply?payOrderApply)?{
????????log.info("Controller?POST請求:"+payOrderApply.toString());
????}
?
????@RepeatDaMie(second?=?1000,describe?=?"大哥,你冷靜點")
????@GetMapping(value?=?"/doGet")
????@ResponseBody
????public?void?doGet(?PayOrderApply?payOrderApply)?{
????????log.info("Controller?GET請求:"+payOrderApply.toString());
????}
?
}

PayOrderApply.java

/**
?*?@Author:?JCccc
?*?@Date:?2022-6-12?9:46
?*?@Description:
?*/
public?class?PayOrderApply?{
?
????private?String?sn;
????private?Long?amount;
????private?String?proCode;
?
????public?String?getSn()?{
????????return?sn;
????}
?
????public?void?setSn(String?sn)?{
????????this.sn?=?sn;
????}
?
????public?Long?getAmount()?{
????????return?amount;
????}
?
????public?void?setAmount(Long?amount)?{
????????this.amount?=?amount;
????}
?
????public?String?getProCode()?{
????????return?proCode;
????}
?
????public?void?setProCode(String?proCode)?{
????????this.proCode?=?proCode;
????}
?
????@Override
????public?String?toString()?{
????????return?"PayOrderApply{"?+
????????????????"sn='"?+?sn?+?'\''?+
????????????????",?amount="?+?amount?+
????????????????",?proCode='"?+?proCode?+?'\''?+
????????????????'}';
????}
}

Springboot怎么利用Redis實現接口冪等性攔截  springboot 第4張

redis生成了值:

Springboot怎么利用Redis實現接口冪等性攔截  springboot 第5張

Springboot怎么利用Redis實現接口冪等性攔截  springboot 第6張

以上就是“Springboot怎么利用Redis實現接口冪等性攔截”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注蝸牛博客行業資訊頻道。

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:niceseo99@gmail.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

評論

日本韩欧美一级A片在线观看