手写redis@Cacheable注解 支持过期时间设置方式

目录
  • 原理解释 
  • 实现方法
  • 源代码

原理解释 

友情链接  手写redis @ cacheable注解参数java对象作为键值 

@cacheable注解作用,将带有该注解方法的返回值存放到redis的的中;

使用方法在方法上使用@cacheable(键=“测试+#p0 + p1#…”)

表示键值为测试+方法第一个参数+方法第二个参数,值为该方法的返回值。

以下源代码表示获取人员列表,redis的中存放的关键值为’领袖’+ leadergroupid + uuid + yeardetailid

        @override
	@cacheable(key="'leader'+#p0+#p1+#p2",value="leader")
	public list<leader> listleaders(string leadergroupid, string uuid, string yeardetailid) {
		return sysindexmapper.listleaders(leadergroupid, uuid, yeardetailid);
	}

等同于

        @override
	public list<leader> listleaders(string leadergroupid, string uuid, string yeardetailid) {
		string key = "leader" + leadergroupid + uuid + yeardetailid;
		// 判断缓存是否存在redis中
		boolean haskey = redisutil.haskey(key);
		if (haskey) {
                        //如果存在 返还redis中的值
			object leaderslist = redisutil.get(key);
			return (list<leader>) leaderslist;
		} else {
			list<leader> leadersquotadetaillist = sysindexmapper.listleaders(leadergroupid, uuid, yeardetailid);
                        //将查询结果存放在redis中
			redisutil.set(key, leadersquotadetaillist);
			return leadersquotadetaillist;
		}
	}

说白了就是在原方法的前面判断的关键值是否存在的redis的中,如果存在就取内存中的值,如果不存在就查询数据库,将查询结果存放在redis的的中。

实现方法

  • 使用代理模式,在方法执行前和执行后可以添加其他处理程序,本文采用springaop +注解方式。
  • 集成redis,封装redis工具类
  • 原版本不支持 过期时间 设置,本文将实现

源代码

缓存配置类redisconfig

package com.huajie.config; 
import org.springframework.beans.factory.annotation.value;
import org.springframework.cache.cachemanager;
import org.springframework.cache.annotation.cachingconfigurersupport;
import org.springframework.cache.annotation.enablecaching;
import org.springframework.cache.interceptor.keygenerator;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.data.redis.cache.rediscachemanager;
import org.springframework.data.redis.connection.redisconnectionfactory;
import org.springframework.data.redis.core.redistemplate;
import org.springframework.data.redis.core.stringredistemplate;
import org.springframework.data.redis.serializer.jackson2jsonredisserializer;
import org.springframework.data.redis.serializer.stringredisserializer;
import com.fasterxml.jackson.annotation.jsonautodetect;
import com.fasterxml.jackson.annotation.propertyaccessor;
import com.fasterxml.jackson.databind.objectmapper;
 
/**
 * redis缓存配置类
 */
@configuration
@enablecaching
public class redisconfig extends cachingconfigurersupport {
 
	@value("${spring.redis.host}")
	private string host;
	@value("${spring.redis.port}")
	private int port;
	@value("${spring.redis.timeout}")
	private int timeout;
 
	// 自定义缓存key生成策略
	@bean
	public keygenerator keygenerator() {
		return new keygenerator() {
			@override
			public object generate(object target, java.lang.reflect.method method, object... params) {
				stringbuffer sb = new stringbuffer();
				sb.append(target.getclass().getname());
				sb.append(method.getname());
				for (object obj : params) {
					sb.append(obj.tostring());
				}
				return sb.tostring();
			}
		};
	}
 
	// 缓存管理器
	@bean
	public cachemanager cachemanager(@suppresswarnings("rawtypes") redistemplate redistemplate) {
		rediscachemanager cachemanager = new rediscachemanager(redistemplate);
		// 设置缓存过期时间
		cachemanager.setdefaultexpiration(10000);
		return cachemanager;
	}
 
	@bean
	public redistemplate<string, object> redistemplate(redisconnectionfactory factory) {
		redistemplate<string, object> template = new redistemplate<string, object>();
		template.setconnectionfactory(factory);
		jackson2jsonredisserializer jackson2jsonredisserializer = new jackson2jsonredisserializer(object.class);
		objectmapper om = new objectmapper();
		om.setvisibility(propertyaccessor.all, jsonautodetect.visibility.any);
		om.enabledefaulttyping(objectmapper.defaulttyping.non_final);
		jackson2jsonredisserializer.setobjectmapper(om);
		stringredisserializer stringredisserializer = new stringredisserializer();
		// key采用string的序列化方式
		template.setkeyserializer(stringredisserializer);
		// hash的key也采用string的序列化方式
		template.sethashkeyserializer(stringredisserializer);
		// value序列化方式采用jackson
		template.setvalueserializer(jackson2jsonredisserializer);
		// hash的value序列化方式采用jackson
		template.sethashvalueserializer(jackson2jsonredisserializer);
		template.afterpropertiesset();
		return template;
	}
 
	private void setserializer(stringredistemplate template) {
		@suppresswarnings({ "rawtypes", "unchecked" })
		jackson2jsonredisserializer jackson2jsonredisserializer = new jackson2jsonredisserializer(object.class);
		objectmapper om = new objectmapper();
		om.setvisibility(propertyaccessor.all, jsonautodetect.visibility.any);
		om.enabledefaulttyping(objectmapper.defaulttyping.non_final);
		jackson2jsonredisserializer.setobjectmapper(om);
		template.setvalueserializer(jackson2jsonredisserializer);
	}
}

redis的依赖引入,配置文件,工具类redisutil,网上几个版本都类似,本文参考以下版本传送门

准备工作做好之后开始正式编写注解@cacheable nextkey()用做二级缓存本文中不会用到

nextkey用法详情>

创建的java的注解@extcacheable  

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 extcacheable {	
	string key() default "";	
	string nextkey() default ""; 
	int expiretime() default 1800;//30分钟	
}
 

springaop切面cacheableaspect

package com.huajie.aspect; 
import java.lang.reflect.method;
import java.util.arraylist;
import java.util.list;
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 com.huajie.annotation.extcacheable;
import com.huajie.utils.redisutil;
 
/**
 * redis缓存处理
 * 不适用与内部方法调用(this.)或者private
 */
@component
@aspect
public class cacheableaspect {	
	@autowired
	private redisutil redisutil;
 
	@pointcut("@annotation(com.huajie.annotation.extcacheable)")
	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 = 1800;
		try {
			// 得到访问的方法对象
			method method = classname.getmethod(methodname, argclass);
			method.setaccessible(true);
			// 判断是否存在@extcacheable注解
			if (method.isannotationpresent(extcacheable.class)) {
				extcacheable annotation = method.getannotation(extcacheable.class);
				key = getrediskey(args,annotation);
				expiretime = getexpiretime(annotation);
			}
		} catch (exception e) {
			throw new runtimeexception("redis缓存注解参数异常", e);
		}
		// 获取缓存是否存在
		boolean haskey = redisutil.haskey(key);
		if (haskey) {
			return redisutil.get(key);
		} else {
                         //执行原方法(java反射执行method获取结果)
			object res = joinpoint.proceed();
                         //设置缓存
			redisutil.set(key, res);
                         //设置过期时间
			redisutil.expire(key, expiretime);
			return res;
		}
	}
	
	private int getexpiretime(extcacheable annotation) {
		return annotation.expiretime();
	}
 
	private string getrediskey(object[] args,extcacheable annotation) {
		string primalkey = annotation.key();
		//获取#p0...集合
		list<string> keylist = getkeyparslist(primalkey);
		for (string keyname : keylist) {
			int keyindex = integer.parseint(keyname.tolowercase().replace("#p", ""));
			object parvalue = args[keyindex];
			primalkey = primalkey.replace(keyname, string.valueof(parvalue));
		}
		return primalkey.replace("+","").replace("'","");
	}
 
	// 获取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;
	}	
}

业务模块使用方法

@override
	@extcacheable(key = "leaders+#p0+#p1+#p2")
	// 手机端获取领导人员列表
	public list<leader> listleaders(string leadergroupid, string uuid, string yeardetailid) {
		list<leader> leadersquotadetaillist = sysindexmapper.listleaders(leadergroupid, uuid, yeardetailid);
		return leadersquotadetaillist;
	}

业务模块过期时间使用方法,5分钟过期

@override
	@extcacheable(key = "mobilecacheflag", expiretime = 60 * 5)
	public int cacheflag() {
		int mobilecacheflag = 1;
		mobilecacheflag = sysindexmapper.cacheflag();
		return mobilecacheflag;
	}

redis的的截图

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

(0)
上一篇 2022年3月21日
下一篇 2022年3月21日

相关推荐