手写redis@Cacheable注解|手写redis@Cacheable注解 参数java对象作为key值详解
目录
- 1.实现方式说明
- 1.1问题说明
- 1.2实现步骤
- 2.源代码
- 3.测试
1.实现方式说明 本文在---- 手写redis @ Cacheable注解支持过期时间设置的基础之上进行扩展。
1.1问题说明
@ Cacheable(key = “'leader'+#p0 +#p1 +#p2” )一般用法,#p0表示方法的第一个参数,#p1表示第二个参数,以此类推。
目前方法的第一个参数为Java的对象,但是原注解只支持Java的的基本数据类型。
1.2实现步骤
1.在原注解中加入新的参数,
【手写redis@Cacheable注解|手写redis@Cacheable注解 参数java对象作为key值详解】 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@Slf4jpublic class NewCacheableAspect { @Autowiredprivate RedisUtil redisUtil; @Pointcut("@annotation(com.huajie.annotation.NewCacheable)")public void annotationPointcut() {} @Around("annotationPointcut()")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {// 获得当前访问的classClass> 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 keyList = getKeyParsList(primalKey); for (String keyName : keyList) {int keyIndex = Integer.parseInt(keyName.toLowerCase().replace("#p", "")); Object parValue = https://www.it610.com/article/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 getKeyParsList(String key) {List ListPar = new ArrayList(); 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 ListgetTreeData() {QuotaManage quotaManage = new QuotaManage(); quotaManage.setQuotaName("测试22222"); List list= this.quotaManageService.queryQuotaTreeData(quotaManage); return list; }
实现层objectIndexArray中的{0}表示第0个参数,objectFieldArray中的“quotaName”表示对应对象中的字段名称
@Override @NewCacheable(key="test+#p0",objectIndexArray = {0},objectFieldArray = {"quotaName"}) public ListqueryQuotaTreeData(QuotaManage quotaManage) {List returnNodesList = new ArrayList (); List nodeList = this.mapper.queryQuotaTreeData(); returnNodesList = treeUtils.getParentList(nodeList); log.info(nodeList.size()+""); return returnNodesList; }
控制台截图拼接的get方法名称和获取的字段值
文章图片
Redis的截图
文章图片
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
推荐阅读
- Android 注解框架对比
- Android注解支持(Support Annotations) (转)
- PowerPoint2010图文详细教程之手写板输入复杂公式_PowerPoint专区
- wps怎么手写署名
- TypeScript使用类型注解 – TypeScript开发教程
- 打开win8手写输入公式技巧图文详细教程
- 这篇手写|这篇手写 Promise 你一定要康康
- 《手写Mybatis》第4章(Mapper|《手写Mybatis》第4章:Mapper XML的解析和注册使用)
- 移动web canvas手写签名结合layui弹窗制作全屏签名
- 关于@Valid注解大全以及用法规范