目录
- 原理解释
- 实现方法
- 源代码
原理解释
友情链接 手写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。