欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

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

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

redis的截图

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

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