经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » Redis » 查看文章
手写redis@Cacheable注解?支持过期时间设置方式
来源:jb51  时间:2022/1/3 14:29:32  对本文有异议

原理解释 

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

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

使用方法在方法上使用@Cacheable(键=“测试+#P0 + P1#...”)

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

以下源代码表示获取人员列表,Redis的中存放的关键值为'领袖'+ leaderGroupId + UUID + yearDetailId

  1. @Override
  2. @Cacheable(key="'leader'+#p0+#p1+#p2",value="leader")
  3. public List<Leader> listLeaders(String leaderGroupId, String uuid, String yearDetailId) {
  4. return sysIndexMapper.listLeaders(leaderGroupId, uuid, yearDetailId);
  5. }

等同于

  1. @Override
  2. public List<Leader> listLeaders(String leaderGroupId, String uuid, String yearDetailId) {
  3. String key = "leader" + leaderGroupId + uuid + yearDetailId;
  4. // 判断缓存是否存在redis中
  5. boolean hasKey = redisUtil.hasKey(key);
  6. if (hasKey) {
  7. //如果存在 返还redis中的值
  8. Object leadersList = redisUtil.get(key);
  9. return (List<Leader>) leadersList;
  10. } else {
  11. List<Leader> leadersQuotaDetailList = sysIndexMapper.listLeaders(leaderGroupId, uuid, yearDetailId);
  12. //将查询结果存放在redis中
  13. redisUtil.set(key, leadersQuotaDetailList);
  14. return leadersQuotaDetailList;
  15. }
  16. }

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

实现方法

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

源代码

缓存配置类RedisConfig

  1. package com.huajie.config;
  2. import org.springframework.beans.factory.annotation.Value;
  3. import org.springframework.cache.CacheManager;
  4. import org.springframework.cache.annotation.CachingConfigurerSupport;
  5. import org.springframework.cache.annotation.EnableCaching;
  6. import org.springframework.cache.interceptor.KeyGenerator;
  7. import org.springframework.context.annotation.Bean;
  8. import org.springframework.context.annotation.Configuration;
  9. import org.springframework.data.redis.cache.RedisCacheManager;
  10. import org.springframework.data.redis.connection.RedisConnectionFactory;
  11. import org.springframework.data.redis.core.RedisTemplate;
  12. import org.springframework.data.redis.core.StringRedisTemplate;
  13. import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
  14. import org.springframework.data.redis.serializer.StringRedisSerializer;
  15. import com.fasterxml.jackson.annotation.JsonAutoDetect;
  16. import com.fasterxml.jackson.annotation.PropertyAccessor;
  17. import com.fasterxml.jackson.databind.ObjectMapper;
  18. /**
  19. * Redis缓存配置类
  20. */
  21. @Configuration
  22. @EnableCaching
  23. public class RedisConfig extends CachingConfigurerSupport {
  24. @Value("${spring.redis.host}")
  25. private String host;
  26. @Value("${spring.redis.port}")
  27. private int port;
  28. @Value("${spring.redis.timeout}")
  29. private int timeout;
  30. // 自定义缓存key生成策略
  31. @Bean
  32. public KeyGenerator keyGenerator() {
  33. return new KeyGenerator() {
  34. @Override
  35. public Object generate(Object target, java.lang.reflect.Method method, Object... params) {
  36. StringBuffer sb = new StringBuffer();
  37. sb.append(target.getClass().getName());
  38. sb.append(method.getName());
  39. for (Object obj : params) {
  40. sb.append(obj.toString());
  41. }
  42. return sb.toString();
  43. }
  44. };
  45. }
  46. // 缓存管理器
  47. @Bean
  48. public CacheManager cacheManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {
  49. RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
  50. // 设置缓存过期时间
  51. cacheManager.setDefaultExpiration(10000);
  52. return cacheManager;
  53. }
  54. @Bean
  55. public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
  56. RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
  57. template.setConnectionFactory(factory);
  58. Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
  59. ObjectMapper om = new ObjectMapper();
  60. om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
  61. om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
  62. jackson2JsonRedisSerializer.setObjectMapper(om);
  63. StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
  64. // key采用String的序列化方式
  65. template.setKeySerializer(stringRedisSerializer);
  66. // hash的key也采用String的序列化方式
  67. template.setHashKeySerializer(stringRedisSerializer);
  68. // value序列化方式采用jackson
  69. template.setValueSerializer(jackson2JsonRedisSerializer);
  70. // hash的value序列化方式采用jackson
  71. template.setHashValueSerializer(jackson2JsonRedisSerializer);
  72. template.afterPropertiesSet();
  73. return template;
  74. }
  75. private void setSerializer(StringRedisTemplate template) {
  76. @SuppressWarnings({ "rawtypes", "unchecked" })
  77. Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
  78. ObjectMapper om = new ObjectMapper();
  79. om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
  80. om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
  81. jackson2JsonRedisSerializer.setObjectMapper(om);
  82. template.setValueSerializer(jackson2JsonRedisSerializer);
  83. }
  84. }

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

https://www.jb51.net/article/233562.htm

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

nextKey用法详情> 设计模式(实战) - 责任链模式 <

创建的Java的注解@ExtCacheable  

  1. package com.huajie.annotation;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Target({ ElementType.METHOD })
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface ExtCacheable {
  9. String key() default "";
  10. String nextKey() default "";
  11. int expireTime() default 1800;//30分钟
  12. }
  1.  

SpringAop切面CacheableAspect

  1. package com.huajie.aspect;
  2. import java.lang.reflect.Method;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. import org.aspectj.lang.ProceedingJoinPoint;
  6. import org.aspectj.lang.annotation.Around;
  7. import org.aspectj.lang.annotation.Aspect;
  8. import org.aspectj.lang.annotation.Pointcut;
  9. import org.aspectj.lang.reflect.MethodSignature;
  10. import org.springframework.beans.factory.annotation.Autowired;
  11. import org.springframework.stereotype.Component;
  12. import com.huajie.annotation.ExtCacheable;
  13. import com.huajie.utils.RedisUtil;
  14. /**
  15. * redis缓存处理
  16. * 不适用与内部方法调用(this.)或者private
  17. */
  18. @Component
  19. @Aspect
  20. public class CacheableAspect {
  21. @Autowired
  22. private RedisUtil redisUtil;
  23. @Pointcut("@annotation(com.huajie.annotation.ExtCacheable)")
  24. public void annotationPointcut() {
  25. }
  26. @Around("annotationPointcut()")
  27. public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
  28. // 获得当前访问的class
  29. Class<?> className = joinPoint.getTarget().getClass();
  30. // 获得访问的方法名
  31. String methodName = joinPoint.getSignature().getName();
  32. // 得到方法的参数的类型
  33. Class<?>[] argClass = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
  34. Object[] args = joinPoint.getArgs();
  35. String key = "";
  36. int expireTime = 1800;
  37. try {
  38. // 得到访问的方法对象
  39. Method method = className.getMethod(methodName, argClass);
  40. method.setAccessible(true);
  41. // 判断是否存在@ExtCacheable注解
  42. if (method.isAnnotationPresent(ExtCacheable.class)) {
  43. ExtCacheable annotation = method.getAnnotation(ExtCacheable.class);
  44. key = getRedisKey(args,annotation);
  45. expireTime = getExpireTime(annotation);
  46. }
  47. } catch (Exception e) {
  48. throw new RuntimeException("redis缓存注解参数异常", e);
  49. }
  50. // 获取缓存是否存在
  51. boolean hasKey = redisUtil.hasKey(key);
  52. if (hasKey) {
  53. return redisUtil.get(key);
  54. } else {
  55. //执行原方法(java反射执行method获取结果)
  56. Object res = joinPoint.proceed();
  57. //设置缓存
  58. redisUtil.set(key, res);
  59. //设置过期时间
  60. redisUtil.expire(key, expireTime);
  61. return res;
  62. }
  63. }
  64. private int getExpireTime(ExtCacheable annotation) {
  65. return annotation.expireTime();
  66. }
  67. private String getRedisKey(Object[] args,ExtCacheable annotation) {
  68. String primalKey = annotation.key();
  69. //获取#p0...集合
  70. List<String> keyList = getKeyParsList(primalKey);
  71. for (String keyName : keyList) {
  72. int keyIndex = Integer.parseInt(keyName.toLowerCase().replace("#p", ""));
  73. Object parValue = args[keyIndex];
  74. primalKey = primalKey.replace(keyName, String.valueOf(parValue));
  75. }
  76. return primalKey.replace("+","").replace("'","");
  77. }
  78. // 获取key中#p0中的参数名称
  79. private static List<String> getKeyParsList(String key) {
  80. List<String> ListPar = new ArrayList<String>();
  81. if (key.indexOf("#") >= 0) {
  82. int plusIndex = key.substring(key.indexOf("#")).indexOf("+");
  83. int indexNext = 0;
  84. String parName = "";
  85. int indexPre = key.indexOf("#");
  86. if(plusIndex>0){
  87. indexNext = key.indexOf("#") + key.substring(key.indexOf("#")).indexOf("+");
  88. parName = key.substring(indexPre, indexNext);
  89. }else{
  90. parName = key.substring(indexPre);
  91. }
  92. ListPar.add(parName.trim());
  93. key = key.substring(indexNext + 1);
  94. if (key.indexOf("#") >= 0) {
  95. ListPar.addAll(getKeyParsList(key));
  96. }
  97. }
  98. return ListPar;
  99. }
  100. }

业务模块使用方法

  1. @Override
  2. @ExtCacheable(key = "Leaders+#p0+#p1+#p2")
  3. // 手机端获取领导人员列表
  4. public List<Leader> listLeaders(String leaderGroupId, String uuid, String yearDetailId) {
  5. List<Leader> leadersQuotaDetailList = sysIndexMapper.listLeaders(leaderGroupId, uuid, yearDetailId);
  6. return leadersQuotaDetailList;
  7. }

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

  1. @Override
  2. @ExtCacheable(key = "mobileCacheFlag", expireTime = 60 * 5)
  3. public int cacheFlag() {
  4. int mobileCacheFlag = 1;
  5. mobileCacheFlag = sysIndexMapper.cacheFlag();
  6. return mobileCacheFlag;
  7. }

Redis的的截图

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

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号