打造redis缓存组件
使用热插拔aop+反射+redis自定义注解+spring el表达式打造redis缓存组件,优雅重构缓存代码
redis配置
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.context.annotation.enableaspectjautoproxy;
import org.springframework.data.redis.connection.lettuce.lettuceconnectionfactory;
import org.springframework.data.redis.core.redistemplate;
import org.springframework.data.redis.serializer.genericjackson2jsonredisserializer;
import org.springframework.data.redis.serializer.stringredisserializer;
@configuration
@enableaspectjautoproxy //v2 开启aop自动代理
public class redisconfig
{
/**
* @param lettuceconnectionfactory
* @return
*
* redis序列化的工具配置类,下面这个请一定开启配置
* 127.0.0.1:6379> keys *
* 1) "ord:102" 序列化过
* 2) "\xac\xed\x00\x05t\x00\aord:102" 野生,没有序列化过
*/
@bean
public redistemplate<string, object> redistemplate(lettuceconnectionfactory lettuceconnectionfactory)
{
redistemplate<string,object> redistemplate = new redistemplate<>();
redistemplate.setconnectionfactory(lettuceconnectionfactory);
//设置key序列化方式string
redistemplate.setkeyserializer(new stringredisserializer());
//设置value的序列化方式json
redistemplate.setvalueserializer(new genericjackson2jsonredisserializer());
redistemplate.sethashkeyserializer(new stringredisserializer());
redistemplate.sethashvalueserializer(new genericjackson2jsonredisserializer());
redistemplate.afterpropertiesset();
return redistemplate;
}
}自定义注解
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 myrediscache //@enableaspectjautoproxy //启aop自动代理
{
//约等于键的前缀prefix,
string keyprefix();
//springel表达式,解析占位符对应的匹配value值
string matchvalue();
}aop
import jakarta.annotation.resource;
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.springframework.core.defaultparameternamediscoverer;
import org.springframework.data.redis.core.redistemplate;
import org.springframework.expression.evaluationcontext;
import org.springframework.expression.expression;
import org.springframework.expression.expressionparser;
import org.springframework.expression.spel.standard.spelexpressionparser;
import org.springframework.expression.spel.support.standardevaluationcontext;
import org.springframework.stereotype.component;
import org.aspectj.lang.reflect.methodsignature;
import java.lang.reflect.method;
import java.util.objects;
@component
@aspect
public class myrediscacheaspect
{
@resource
private redistemplate redistemplate;
//配置织入点
@pointcut("@annotation(com.atguigu.interview2.annotations.myrediscache)")
public void cachepointcut(){}
@around("cachepointcut()")
public object docache(proceedingjoinpoint joinpoint)
{
object result = null;
/**
* @myrediscache(keyprefix = "user",matchvalue = "#id")
* public user getuserbyid(integer id)
* {
* return usermapper.selectbyprimarykey(id);
* }
*/
try
{
//1 获得重载后的方法名
methodsignature signature = (methodsignature) joinpoint.getsignature();
method method = signature.getmethod();
//2 确定方法名后获得该方法上面配置的注解标签myrediscache
myrediscache myrediscacheannotation = method.getannotation(myrediscache.class);
//3 拿到了myrediscache这个注解标签,获得该注解上面配置的参数进行封装和调用
string keyprefix = myrediscacheannotation.keyprefix();
string matchvaluespringel = myrediscacheannotation.matchvalue();
//4 springel 解析器
expressionparser parser = new spelexpressionparser();
expression expression = parser.parseexpression(matchvaluespringel);//#id
evaluationcontext context = new standardevaluationcontext();
//5 获得方法里面的形参个数
object[] args = joinpoint.getargs();
defaultparameternamediscoverer discoverer = new defaultparameternamediscoverer();
string[] parameternames = discoverer.getparameternames(method);
for (int i = 0; i < parameternames.length; i++)
{
system.out.println("获得方法里参数名和值: "+parameternames[i] + "\t" + args[i].tostring());
context.setvariable(parameternames[i], args[i].tostring());
}
//6 通过上述,拼接redis的最终key形式
string key = keyprefix + ":" + expression.getvalue(context).tostring();
system.out.println("------拼接redis的最终key形式: " + key);
//7 先去redis里面查询看有没有
result = redistemplate.opsforvalue().get(key);
if (result != null)
{
system.out.println("------redis里面有,我直接返回结果不再打扰mysql: " + result);
return result;
}
//8 redis里面没有,去找msyql查询或叫进行后续业务逻辑
//-------aop精华部分,才去找finduserbyid方法干活
//usermapper.selectbyprimarykey(id);
result = joinpoint.proceed();//主业务逻辑查询mysql,放行放行放行
//9 mysql步骤结束,还需要把结果存入redis一次,缓存补偿
if (result != null)
{
system.out.println("------redis里面无,还需要把结果存入redis一次,缓存补偿: " + result);
redistemplate.opsforvalue().set(key, result);
}
} catch (throwable throwable) {
throwable.printstacktrace();
}
return result;
}
}测试
/**
* 会将返回值存进redis里,key生成规则需要程序员用spel表达式自己指定,value就是程序从mysql查出并返回的user
* redis的key 等于 keyprefix:matchvalue
*/
@override
@myrediscache(keyprefix = "user",matchvalue = "#id")
public user getuserbyid(integer id)
{
return usermapper.selectbyprimarykey(id);
}总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论