手写redis@Cacheable注解 支持过期时间设置

手写redis@Cacheable注解 支持过期时间设置原理解释 友情链接手写 redis Cacheable 注解参数 java 对象作为键值 Cacheable 注解作用 将带有该注解方法的返回值存放到 redis 的的中 使用方法在方法上使用 Cacheable 键 测试 P0 P1 表示键值为测试 方法第一个参数 方法第二个参数 值为该方法的返回值 以下源代码表示获取人员列表 Redis 的中存放的关键值为

  • 原理解释 

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

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

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

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

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

 @Override @Cacheable(key="'leader'+#p0+#p1+#p2",value="leader") public List 
  
    listLeaders(String leaderGroupId, String uuid, String yearDetailId) { return sysIndexMapper.listLeaders(leaderGroupId, uuid, yearDetailId); } 
  

等同于

 @Override public List 
  
    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 
   
     ) leadersList; } else { List 
    
      leadersQuotaDetailList = sysIndexMapper.listLeaders(leaderGroupId, uuid, yearDetailId); //将查询结果存放在redis中 redisUtil.set(key, leadersQuotaDetailList); return leadersQuotaDetailList; } } 
     
    
  

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

  • 实现方法

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

缓存配置类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 
  
    redisTemplate(RedisConnectionFactory factory) { RedisTemplate 
   
     template = new RedisTemplate 
    
      (); 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,网上几个版本都类似,本文参考以下版本传送门

https://www.cnblogs.com/zeng1994/p/03303c805731afc9aa9c60dbbd32a323.html

准备工作做好之后开始正式编写注解@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 
  
    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 
   
     getKeyParsList(String key) { List 
    
      ListPar = new ArrayList 
     
       (); 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 
  
    listLeaders(String leaderGroupId, String uuid, String yearDetailId) { List 
   
     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的的截图

手写redis@Cacheable注解 支持过期时间设置

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/224875.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 上午10:56
下一篇 2026年3月17日 上午10:56


相关推荐

  • 贴片机保养步骤 保养项目 保养的目的与作用

    贴片机是一种高强度、长时间、超负荷的运转设备,需要定期进行细心的呵护保养,贴片机保养的好,运转稳定,保证日常生产产能,贴片机如衣食父母;贴片机保养不到位或经常不保养,性能再好的机子也会不堪重负出现各类毛病,影响生产产能和产品质量,因此贴片机保养是贴片厂非常重要的一环工作内容事项。(延伸阅读:何为SMT?smt是做什么的,smt贴片是什么意思?)贴片机日保用干净白布清洁机器表面灰尘,包含机身、显示器、键盘、鼠标、开关等;检测气压正常值;检查机器内部各装置是否正常;用吸尘器清洁机器里

    2022年4月7日
    40
  • Thread之ParameterizedThreadStart

    Thread之ParameterizedThreadStartTreadStart委托仅仅指向无参数且无返回值的方法。如果新线程上运行带参数的方法,那么需要用到ParameterizedThreadStart委托,ParameterizedThreadStart定义:publicdelegatevoidParameterizedThreadStart(objectobj);一:要求只能有一个参数,且为object类型二:无返回值T…

    2022年7月15日
    22
  • MATLAB—-输入和输出

    MATLAB—-输入和输出文章目录 1 输入语句 1 1 输入数值或矩阵 1 2 输入字符串 2 输出语句 2 1 输出单个字段 2 2 输出多个字段 1 输入语句 1 1 输入数值或矩阵 value1 input 请输入一个数值 value2 input 请输入一个矩阵 1 2 输入字符串输入字符串 需要加第二个参数 s string input 请输入一个字符串 s 2 输出语句使用 disp 函数可以输出 输出多个字段时 需要将多个字段转

    2025年7月2日
    4
  • claude code新手使用教程

    claude code新手使用教程

    2026年3月15日
    3
  • 国产linux操作系统深度系统20.3发布(推荐)

    国产linux操作系统深度系统20.3发布(推荐)深度操作系统(deepin)是一个致力于为全球用户提供美观易用、安全稳定服务的Linux发行版,同时也一直是排名最高的来自中国团队研发的Linux发行版。(了解deepin国际排名)深度操作系统20.3,升级Stable内核到5.15版本,进一步提升兼容性。修复系统安全漏洞,增强安全性。部分深度应用新增及优化常用功能,满足不同场景下的使用需求。针对桌面环境部分问题修复及优化,只为带来更好体验。更强大的内核版本Stable内核升级到5.15版本,增强对Intel12代U和NTFS文件系统的支

    2022年5月16日
    46
  • 微信小程序+java后台[通俗易懂]

    微信小程序+java后台[通俗易懂]      博主是大四学生,毕业设计做的是微信小程序+java后台。陆陆续续经历了三个月(因为白天要实习又碰上过年玩了一阵子),从对微信小程序一无所知到完成毕设,碰到许多问题,在跟大家分享一下自己的经历和一个小程序怎么从零开始。希望像我一样的小程序初学者看了这篇文章,可以少花费一些不必要的时间,少踩一点坑。          开发一个微信小程序需要https域名以及服务器,博主购买的是阿里…

    2022年7月8日
    49

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号