打造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); }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论