如何打造redis缓存组件
目录
- 打造redis缓存组件
- redis配置
- 自定义注解
- AOP
- 测试
- 总结
打造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); }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持电脑手机教程网。