手写redis@Cacheable注解 参数java对象作为key值详解

作者:不懂的浪漫 时间:2022-04-26 11:30:36 

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整体流程没有任何变化

主要是关键值获取的方式,发生了变化

手写redis@Cacheable注解 参数java对象作为key值详解

使用Java的反射技术

手写redis@Cacheable注解 参数java对象作为key值详解

完整代码如下:

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中的&ldquo;quotaName&rdquo;表示对应对象中的字段名称

@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@Cacheable注解 参数java对象作为key值详解

Redis的截图

手写redis@Cacheable注解 参数java对象作为key值详解

来源:https://blog.csdn.net/xiewenfeng520/article/details/84864120

标签:@Cacheable,参数,对象,key值
0
投稿

猜你喜欢

  • java,android,MD5加密算法的实现代码(16位,32位)

    2022-07-12 20:40:10
  • SpringBoot返回Json对象报错(返回对象为空{})

    2022-06-30 03:31:19
  • Spring Security基本原理详解

    2022-12-24 15:57:54
  • Java高性能序列化工具Kryo详情

    2021-11-02 16:42:00
  • Java中使用StackWalker和Stream API进行堆栈遍历

    2023-04-12 11:29:07
  • java开源调度如何给xxljob加k8s执行器

    2021-09-17 16:41:50
  • JavaWeb开发之使用jQuery与Ajax实现动态联级菜单效果

    2023-11-28 19:46:08
  • 老生常谈java中cookie的使用

    2023-11-11 04:37:59
  • 基于idea Maven中的redis配置使用详解

    2023-11-29 11:57:28
  • Springboot详解实现食品仓库管理系统流程

    2023-11-10 18:33:15
  • 详解Mybatis通用Mapper介绍与使用

    2023-11-29 08:49:08
  • java实现向有序数组中插入一个元素实例

    2023-04-03 23:10:49
  • Java 二分查找的实现及图例解析

    2023-10-28 11:41:57
  • C#实现字符串进制转换方法汇总

    2022-01-03 09:30:37
  • 深入理解Java中的接口

    2023-11-08 23:52:43
  • java中的实体类时间格式化

    2022-05-18 02:18:19
  • Java的SPI机制实例详解

    2021-08-15 08:47:41
  • 教你怎么用java一键自动生成数据库文档

    2021-08-01 02:34:36
  • C#获取Windows进程监听的TCP/UDP端口实例

    2021-11-20 13:06:21
  • Opencv光流运动物体追踪详解

    2023-06-21 11:55:31
  • asp之家 软件编程 m.aspxhome.com