欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

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

来源: javaer 分享于  点击 21630 次 点评:159

手写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.在原注解中加入新的参数,

       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的截图

      以上为个人经验,希望能给大家一个参考,也希望大家多多支持3672js教程。

      您可能感兴趣的文章:
      • 详解Spring缓存注解@Cacheable,@CachePut , @CacheEvict使用
      • spring整合redis缓存并以注解(@Cacheable、@CachePut、@CacheEvict)形式使用
      • @Cacheable 拼接key的操作
      • springboot增加注解缓存@Cacheable的实现
      • 详解Java在redis中进行对象的缓存
      相关栏目:

      用户点评