您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息

Springboot怎么利用Redis实现接口幂等性拦截

2025/4/6 21:29:45发布23次查看
正文
自定义注解 怎么玩的 :      
①标记哪个接口需要进行幂等性拦截        
②每个接口可以要求幂等性范围时间不一样,举例:可以2秒内,可以3秒内,时间自己传        
③ 一旦触发了,提示语可以不同 ,举例:vip的接口,普通用户的接口,提示语不一样(开玩笑)
效果:
实战开始核心三件套注解、拦截器、拦截器配置
① 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: 接口幂等性校验拦截器 */@componentpublic 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: */@configurationpublic 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: */@componentpublic 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; @componentpublic 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@enablecachingpublic 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: */@restcontrollerpublic 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 + '\'' + '}'; }}
redis生成了值:
以上就是springboot怎么利用redis实现接口幂等性拦截的详细内容。
该用户其它信息

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录 Product