手写redis@Cacheable注解 参数java对象作为key值详解
程序员文章站
2022-03-21 14:32:00
目录1.实现方式说明1.1问题说明1.2实现步骤2.源代码3.测试1.实现方式说明本文在---- 手写redis @ cacheable注解支持过期时间设置 的基础之上进行扩展。1.1问题说明@ c...
1.实现方式说明
本文在---- 手写redis @ cacheable注解支持过期时间设置 的基础之上进行扩展。
1.1问题说明
@ cacheable(key = “'leader'+#p0 +#p1 +#p2” )一般用法,#p0表示方法的第一个参数,#p1表示第二个参数,以此类推。
目前方法的第一个参数为java的对象,但是原注解只支持java的的基本数据类型。
1.2实现步骤
1.在原注解中加入新的参数,
objectindexarray表示哪几个角标参数(从0开始)为java对象,objectfieldarray表示对应位置该对象的字段值作为key
2.如何获取参数的对象以及该字段的值
使用的java的反射,拼接get方法获取该字段值。
2.源代码
修改java注解@extcacheable,本文中使用@newcacheable
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 newcacheable { string key() default ""; int[] objectindexarray(); string[] objectfieldarray(); int expiretime() default 1800;//30分钟 }
springaop切面newcacheableaspect
获取aop整体流程没有任何变化
主要是关键值获取的方式,发生了变化
使用java的反射技术
完整代码如下:
package com.huajie.aspect; import com.huajie.annotation.newcacheable; import com.huajie.utils.redisutil; import com.huajie.utils.stringutil; import lombok.extern.slf4j.slf4j; 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 java.lang.reflect.method; import java.util.arraylist; import java.util.list; /** * redis缓存处理 不适用与内部方法调用(this.)或者private */ @component @aspect @slf4j public class newcacheableaspect { @autowired private redisutil redisutil; @pointcut("@annotation(com.huajie.annotation.newcacheable)") 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 = 3600; try { // 得到访问的方法对象 method method = classname.getmethod(methodname, argclass); method.setaccessible(true); // 判断是否存在@extcacheable注解 if (method.isannotationpresent(newcacheable.class)) { newcacheable annotation = method.getannotation(newcacheable.class); key = getrediskey(args, annotation); expiretime = getexpiretime(annotation); } } catch (exception e) { throw new runtimeexception("redis缓存注解参数异常", e); } log.info(key); boolean haskey = redisutil.haskey(key); if (haskey) { return redisutil.get(key); } else { object res = joinpoint.proceed(); redisutil.set(key, res); redisutil.expire(key, expiretime); return res; } } private int getexpiretime(newcacheable annotation) { return annotation.expiretime(); } private string getrediskey(object[] args, newcacheable annotation) throws exception{ string primalkey = annotation.key(); // 获取#p0...集合 list<string> keylist = getkeyparslist(primalkey); for (string keyname : keylist) { int keyindex = integer.parseint(keyname.tolowercase().replace("#p", "")); object parvalue = getparvalue(annotation, keyindex, args); primalkey = primalkey.replace(keyname, string.valueof(parvalue)); } return primalkey.replace("+", "").replace("'", ""); } private object getparvalue(newcacheable annotation, int keyindex, object[] args) throws exception{ int[] objectindexarray = annotation.objectindexarray(); string[] objectfieldarray = annotation.objectfieldarray(); if (existsobject(keyindex, objectindexarray)) { return getparvaluebyobject(args, keyindex, objectfieldarray); } else { return args[keyindex]; } } private object getparvaluebyobject(object[] args, int keyindex, string[] objectfieldarray) throws exception { class cls = args[keyindex].getclass(); method method; if(objectfieldarray!=null&&objectfieldarray.length>=keyindex){ method = cls.getmethod("get" + stringutil.firstchartouppercase(objectfieldarray[keyindex])); }else{ method = cls.getmethod("get" + stringutil.firstchartouppercase(cls.getfields()[0].getname())); } method.setaccessible(true); log.info(method.getname()); return method.invoke(args[keyindex]); } private boolean existsobject(int keyindex, int[] objectindexarray) { if (objectindexarray == null || objectindexarray.length <= 0) { return false; } for (int i = 0; i < objectindexarray.length; i++) { if (keyindex == objectindexarray[i]) { return true; } } return false; } // 获取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; } }
3.测试
业务模块使用方法controller
@requestmapping("queryquotatreedata") @responsebody public list<treenode> gettreedata() { quotamanage quotamanage = new quotamanage(); quotamanage.setquotaname("测试22222"); list<treenode> list = this.quotamanageservice.queryquotatreedata(quotamanage); return list; }
实现层objectindexarray中的{0}表示第0个参数,objectfieldarray中的“quotaname”表示对应对象中的字段名称
@override @newcacheable(key="test+#p0",objectindexarray = {0},objectfieldarray = {"quotaname"}) public list<treenode> queryquotatreedata(quotamanage quotamanage) { list<treenode> returnnodeslist = new arraylist<treenode>(); list<treenode> nodelist = this.mapper.queryquotatreedata(); returnnodeslist = treeutils.getparentlist(nodelist); log.info(nodelist.size()+""); return returnnodeslist; }
控制台截图拼接的get方法名称和获取的字段值
redis的截图
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。