手写redis@Cacheable注解 支持过期时间设置方式
程序员文章站
2022-03-02 17:34:13
目录原理解释实现方法源代码原理解释友情链接 手写redis @ cacheable注解参数java对象作为键值@cacheable注解作用,将带有该注解方法的返回值存放到redis的的中;使用方法在方...
原理解释
友情链接 手写redis @ cacheable注解参数java对象作为键值
@cacheable注解作用,将带有该注解方法的返回值存放到redis的的中;
使用方法在方法上使用@cacheable(键=“测试+#p0 + p1#...”)
表示键值为测试+方法第一个参数+方法第二个参数,值为该方法的返回值。
以下源代码表示获取人员列表,redis的中存放的关键值为'领袖'+ leadergroupid + uuid + yeardetailid
@override @cacheable(key="'leader'+#p0+#p1+#p2",value="leader") public list<leader> listleaders(string leadergroupid, string uuid, string yeardetailid) { return sysindexmapper.listleaders(leadergroupid, uuid, yeardetailid); }
等同于
@override public list<leader> listleaders(string leadergroupid, string uuid, string yeardetailid) { string key = "leader" + leadergroupid + uuid + yeardetailid; // 判断缓存是否存在redis中 boolean haskey = redisutil.haskey(key); if (haskey) { //如果存在 返还redis中的值 object leaderslist = redisutil.get(key); return (list<leader>) leaderslist; } else { list<leader> leadersquotadetaillist = sysindexmapper.listleaders(leadergroupid, uuid, yeardetailid); //将查询结果存放在redis中 redisutil.set(key, leadersquotadetaillist); return leadersquotadetaillist; } }
说白了就是在原方法的前面判断的关键值是否存在的redis的中,如果存在就取内存中的值,如果不存在就查询数据库,将查询结果存放在redis的的中。
实现方法
- 使用代理模式,在方法执行前和执行后可以添加其他处理程序,本文采用springaop +注解方式。
- 集成redis,封装redis工具类
- 原版本不支持 过期时间 设置,本文将实现
源代码
缓存配置类redisconfig
package com.huajie.config; import org.springframework.beans.factory.annotation.value; import org.springframework.cache.cachemanager; import org.springframework.cache.annotation.cachingconfigurersupport; import org.springframework.cache.annotation.enablecaching; import org.springframework.cache.interceptor.keygenerator; import org.springframework.context.annotation.bean; import org.springframework.context.annotation.configuration; 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.stringredisserializer; import com.fasterxml.jackson.annotation.jsonautodetect; import com.fasterxml.jackson.annotation.propertyaccessor; import com.fasterxml.jackson.databind.objectmapper; /** * redis缓存配置类 */ @configuration @enablecaching public class redisconfig extends cachingconfigurersupport { @value("${spring.redis.host}") private string host; @value("${spring.redis.port}") private int port; @value("${spring.redis.timeout}") private int timeout; // 自定义缓存key生成策略 @bean public keygenerator keygenerator() { return new keygenerator() { @override public object generate(object target, java.lang.reflect.method method, object... params) { stringbuffer sb = new stringbuffer(); sb.append(target.getclass().getname()); sb.append(method.getname()); for (object obj : params) { sb.append(obj.tostring()); } return sb.tostring(); } }; } // 缓存管理器 @bean public cachemanager cachemanager(@suppresswarnings("rawtypes") redistemplate redistemplate) { rediscachemanager cachemanager = new rediscachemanager(redistemplate); // 设置缓存过期时间 cachemanager.setdefaultexpiration(10000); return cachemanager; } @bean public redistemplate<string, object> redistemplate(redisconnectionfactory factory) { redistemplate<string, object> template = new redistemplate<string, object>(); template.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); stringredisserializer stringredisserializer = new stringredisserializer(); // key采用string的序列化方式 template.setkeyserializer(stringredisserializer); // hash的key也采用string的序列化方式 template.sethashkeyserializer(stringredisserializer); // value序列化方式采用jackson template.setvalueserializer(jackson2jsonredisserializer); // hash的value序列化方式采用jackson template.sethashvalueserializer(jackson2jsonredisserializer); template.afterpropertiesset(); return template; } private void setserializer(stringredistemplate template) { @suppresswarnings({ "rawtypes", "unchecked" }) 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); template.setvalueserializer(jackson2jsonredisserializer); } }
redis的依赖引入,配置文件,工具类redisutil,网上几个版本都类似,本文参考以下版本传送门
准备工作做好之后开始正式编写注解@cacheable nextkey()用做二级缓存本文中不会用到
nextkey用法详情>
创建的java的注解@extcacheable
package com.huajie.annotation; import java.lang.annotation.elementtype; import java.lang.annotation.retention; import java.lang.annotation.retentionpolicy; import java.lang.annotation.target; @target({ elementtype.method }) @retention(retentionpolicy.runtime) public @interface extcacheable { string key() default ""; string nextkey() default ""; int expiretime() default 1800;//30分钟 }
springaop切面cacheableaspect
package com.huajie.aspect; import java.lang.reflect.method; import java.util.arraylist; import java.util.list; 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.aspectj.lang.reflect.methodsignature; import org.springframework.beans.factory.annotation.autowired; import org.springframework.stereotype.component; import com.huajie.annotation.extcacheable; import com.huajie.utils.redisutil; /** * redis缓存处理 * 不适用与内部方法调用(this.)或者private */ @component @aspect public class cacheableaspect { @autowired private redisutil redisutil; @pointcut("@annotation(com.huajie.annotation.extcacheable)") public void annotationpointcut() { } @around("annotationpointcut()") public object doaround(proceedingjoinpoint joinpoint) throws throwable { // 获得当前访问的class class<?> classname = joinpoint.gettarget().getclass(); // 获得访问的方法名 string methodname = joinpoint.getsignature().getname(); // 得到方法的参数的类型 class<?>[] argclass = ((methodsignature) joinpoint.getsignature()).getparametertypes(); object[] args = joinpoint.getargs(); string key = ""; int expiretime = 1800; try { // 得到访问的方法对象 method method = classname.getmethod(methodname, argclass); method.setaccessible(true); // 判断是否存在@extcacheable注解 if (method.isannotationpresent(extcacheable.class)) { extcacheable annotation = method.getannotation(extcacheable.class); key = getrediskey(args,annotation); expiretime = getexpiretime(annotation); } } catch (exception e) { throw new runtimeexception("redis缓存注解参数异常", e); } // 获取缓存是否存在 boolean haskey = redisutil.haskey(key); if (haskey) { return redisutil.get(key); } else { //执行原方法(java反射执行method获取结果) object res = joinpoint.proceed(); //设置缓存 redisutil.set(key, res); //设置过期时间 redisutil.expire(key, expiretime); return res; } } private int getexpiretime(extcacheable annotation) { return annotation.expiretime(); } private string getrediskey(object[] args,extcacheable annotation) { string primalkey = annotation.key(); //获取#p0...集合 list<string> keylist = getkeyparslist(primalkey); for (string keyname : keylist) { int keyindex = integer.parseint(keyname.tolowercase().replace("#p", "")); object parvalue = args[keyindex]; primalkey = primalkey.replace(keyname, string.valueof(parvalue)); } return primalkey.replace("+","").replace("'",""); } // 获取key中#p0中的参数名称 private static list<string> getkeyparslist(string key) { list<string> listpar = new arraylist<string>(); if (key.indexof("#") >= 0) { int plusindex = key.substring(key.indexof("#")).indexof("+"); int indexnext = 0; string parname = ""; int indexpre = key.indexof("#"); if(plusindex>0){ indexnext = key.indexof("#") + key.substring(key.indexof("#")).indexof("+"); parname = key.substring(indexpre, indexnext); }else{ parname = key.substring(indexpre); } listpar.add(parname.trim()); key = key.substring(indexnext + 1); if (key.indexof("#") >= 0) { listpar.addall(getkeyparslist(key)); } } return listpar; } }
业务模块使用方法
@override @extcacheable(key = "leaders+#p0+#p1+#p2") // 手机端获取*员列表 public list<leader> listleaders(string leadergroupid, string uuid, string yeardetailid) { list<leader> leadersquotadetaillist = sysindexmapper.listleaders(leadergroupid, uuid, yeardetailid); return leadersquotadetaillist; }
业务模块过期时间使用方法,5分钟过期
@override @extcacheable(key = "mobilecacheflag", expiretime = 60 * 5) public int cacheflag() { int mobilecacheflag = 1; mobilecacheflag = sysindexmapper.cacheflag(); return mobilecacheflag; }
redis的的截图
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。
下一篇: Redis 抽奖大转盘的实战示例