手写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@Slf4jpublic 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")@ResponseBodypublic 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的截图

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

【本文转自:香港高防服务器 hkgf.html 复制请保留原URL】造物之前,必先造人。

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

相关文章:

你感兴趣的文章:

标签云: