spring cache ttl 过期实现

一般的,使用 spring cache 时,注解上不支持 ttl 过期时间

@Cacheable(cacheNames = "product3", key = "#id")
@GetMapping("/product3/{id}")
public String getById3(@PathVariable("id") Integer id) {log.info("get from db");return "success";
}

虽然可以在 配置 CacheManager 时进行配置,但每次调整都需要修改配置 文件

@Bean
public CacheManager cacheManager(LettuceConnectionFactory lettuceConnectionFactory ) {RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();redisCacheConfiguration = redisCacheConfiguration.entryTtl(Duration.ofMinutes(30L)) // 设置缓存的默认超时时间:30分钟.disableCachingNullValues() // 如果是空值,不缓存.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string())) // 设置key序列化器.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.java())); // 设置value序列化器return RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(lettuceConnectionFactory)).cacheDefaults(redisCacheConfiguration).build();
}

那如何实现注解上设置ttl呢?

实现

思路:

  • 继承 Cacheable 注解
  • 容器刷新后重新刷新 acheManager.initializeCaches() 方法,重新加载

定义注解 @TTLCacheable

继承 @Cacheable 注解

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Cacheable
@Documented
public @interface TTLCacheable {@AliasFor(annotation = Cacheable.class, value = "value")String[] value() default {};@AliasFor(annotation = Cacheable.class, value = "cacheNames")String[] cacheNames() default {};@AliasFor(annotation = Cacheable.class, value = "key")String key() default "";@AliasFor(annotation = Cacheable.class, value = "keyGenerator")String keyGenerator() default "";@AliasFor(annotation = Cacheable.class, value = "cacheResolver")String cacheResolver() default "";@AliasFor(annotation = Cacheable.class, value = "condition")String condition() default "";@AliasFor(annotation = Cacheable.class, value = "unless")String unless() default "";@AliasFor(annotation = Cacheable.class, value = "sync")boolean sync() default false;/*** cache 过期时间* @return*/int ttl() default 0;
}

刷新ttl时间 TTLCachePostProcessor

public class TTLCachePostProcessor implements BeanPostProcessor, SmartInitializingSingleton, BeanFactoryAware {private BeanFactory beanFactory;private static Set<TTLCacheable> cacheables = new ConcurrentHashSet<>();private RedisTTLCacheConfig redisTTLCacheConfig;public TTLCachePostProcessor(RedisTTLCacheConfig redisTTLCacheConfig) {this.redisTTLCacheConfig = redisTTLCacheConfig;}@Overridepublic void afterSingletonsInstantiated() {if (!CollectionUtils.isEmpty(cacheables)) {CacheManager cm = beanFactory.getBean(CacheManager.class);if (!(cm instanceof RedisCacheManager)){return;}RedisCacheManager cacheManager = (RedisCacheManager) cm;//反射Field field = ReflectUtil.getField(RedisCacheManager.class, "initialCacheConfiguration");field.setAccessible(Boolean.TRUE.booleanValue());Map<String, RedisCacheConfiguration> configMap = (Map<String, RedisCacheConfiguration>) ReflectionUtils.getField(field, cacheManager);Set<String> list = new HashSet<>();for (TTLCacheable cacheable : cacheables) {for (String cache : cacheable.cacheNames()) {if (!list.contains(cache)) {list.add(cache);if (redisTTLCacheConfig!=null){RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();config = config.serializeValuesWith(redisTTLCacheConfig.getValueSerializationPair());config = config.serializeKeysWith(redisTTLCacheConfig.getKeySerializationPair());if (redisTTLCacheConfig.getKeyPrefix() != null) {config = config.computePrefixWith(cacheName -> cacheName.concat(":").concat(redisTTLCacheConfig.getKeyPrefix()).concat(":"));}if (!redisTTLCacheConfig.isCacheNullValues()) {config = config.disableCachingNullValues();}if (!redisTTLCacheConfig.isUseKeyPrefix()) {config = config.disableKeyPrefix();}config = config.entryTtl(Duration.ofSeconds(cacheable.ttl()));configMap.put(cache, config);}else {RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();config = config.entryTtl(Duration.ofSeconds(cacheable.ttl()));configMap.put(cache, config);}}}}cacheManager.initializeCaches();}}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);if (AnnotationUtils.isCandidateClass(targetClass, Arrays.asList(TTLCacheable.class))) {Map<Method, TTLCacheable> annotatedMethods =MethodIntrospector.selectMethods(targetClass, (MethodIntrospector.MetadataLookup<TTLCacheable>) method -> {TTLCacheable ttlCacheables = AnnotatedElementUtils.getMergedAnnotation(method, TTLCacheable.class);return ttlCacheables;});if (!annotatedMethods.isEmpty()) {for (Map.Entry<Method, TTLCacheable> methodSetEntry : annotatedMethods.entrySet()) {if (methodSetEntry.getValue().ttl() > 0) {cacheables.add(methodSetEntry.getValue());}}}}return bean;}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {this.beanFactory = beanFactory;}
}

RedisTTLCacheConfig:

@Data
public class RedisTTLCacheConfig{/*** Entry expiration. By default the entries never expire.*/private Duration timeToLive;/*** Allow caching null values.*/private boolean cacheNullValues = true;/*** Key prefix.*/private String keyPrefix;/*** Whether to use the key prefix when writing to Redis.*/private boolean useKeyPrefix = true;private ConversionService conversionService;/*** 序列化*/private RedisSerializationContext.SerializationPair<String> keySerializationPair;private RedisSerializationContext.SerializationPair<Object> valueSerializationPair;}

自动装配

  • @EnableCacheTTLOperation
  • CacheTTLAutoConfiguration
  • CacheTTLOperationConfigSelector
@EnableCacheTTLOperation
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CacheTTLOperationConfigSelector.class)
public @interface EnableCacheTTLOperation {}
CacheTTLAutoConfiguration
@Configuration
@ConditionalOnClass(value = {RedisTemplate.class, CacheInterceptor.class})
@ConditionalOnBean(RedisTemplate.class)
@EnableConfigurationProperties(value = {CacheProperties.class})
public class CacheTTLAutoConfiguration {@Bean@ConditionalOnMissingBean(RedisTTLCacheConfig.class)public RedisTTLCacheConfig redisTTLCacheConfig(CacheProperties properties, RedisTemplate redisTemplate) {CacheProperties.Redis redisProperties = properties.getRedis();RedisTTLCacheConfig config = new RedisTTLCacheConfig();config.setTimeToLive(redisProperties.getTimeToLive());config.setCacheNullValues(redisProperties.isCacheNullValues());config.setKeyPrefix(redisProperties.getKeyPrefix());config.setUseKeyPrefix(redisProperties.isUseKeyPrefix());config.setKeySerializationPair(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getKeySerializer()));config.setValueSerializationPair(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()));return config;}@Beanpublic TTLCachePostProcessor ttlCachePostProcessor(ObjectProvider<RedisTTLCacheConfig> provider) {return new TTLCachePostProcessor(provider.getIfAvailable());}
}
CacheTTLOperationConfigSelector
public class CacheTTLOperationConfigSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {return new String[]{"com.x.x.CacheTTLAutoConfiguration"};}}

测试

  • 在 boot 启动类加上注解 @EnableCacheTTLOperation
  • 使用 @TTLCacheable 设置 过期时间
@EnableCaching
@EnableCacheTTLOperation
@SpringBootApplication
public class MvcApplication {//omit...
}

Controller

@Slf4j
@RestController
public class CacheController {@TTLCacheable(cacheNames = "product", key = "#id", ttl = 120)@GetMapping("/product/{id}")public String getById(@PathVariable("id") Integer id) {log.info("get from db");return "success";}@TTLCacheable(cacheNames = "product2", key = "#id")@GetMapping("/product2/{id}")public String getById2(@PathVariable("id") Integer id) {log.info("get from db");return "success";}@Cacheable(cacheNames = "product3", key = "#id")@GetMapping("/product3/{id}")public String getById3(@PathVariable("id") Integer id) {log.info("get from db");return "success";}}

good luck!

spring cache ttl 过期相关推荐

  1. 玩转Spring Cache --- 整合进程缓存之王Caffeine Cache和Ehcache3.x【享学Spring】

    每篇一句 人到中年就是一部西游记:悟空的压力,八戒的身材,沙僧的发型,唐僧的唠叨 前言 前面文章大篇幅详细讲解了Spring Cache缓存抽象.三大缓存注解的工作原理等等.若是细心的小伙伴会发现:讲 ...

  2. Spring Cache使用Redis自定义缓存key

    一.什么是Spring Cache 从spring 3.1版本开始,提供了一种透明的方式来为现有的spring 应用添加cache.在应用层面与后端存储之间,提供了一层抽象,这层抽象目的在于封装各种可 ...

  3. redis,Spring Cache,Mango,ES

    目录 Redis Redis下载与安装 Redis常用命令 在Java中操作Redis Jedis Spring Data Redis Spring Cache Spring Cache介绍 Spri ...

  4. 22-08-06 西安 尚医通(03)EasyExcel; Spring Cache 、Redis做缓存

    EasyExcel EasyExcel:一行一行读取到内存 EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单.节省内存著称 POI:java里操作excel,读取.创建excel ...

  5. Spring Cache的基本使用

    文章目录 一.概述 二.Spring Cache 的使用 2.1 环境搭建 2.2 缓存的读模式 @Cacheable 2.3 自定义缓存配置 2.4 @CachePut 2.5 @CacheEvic ...

  6. spring cache基础篇

    Spring从3.1开始定义了org.springframework.cache.Cache和 org.springframework.cache.CacheManager接口来统一不同的缓存技术:并 ...

  7. Spring Cache抽象-使用Java类注解的方式整合EhCache

    概述 工程结构 源码 概述 Spring Cache抽象-之缓存注解这篇博文中我们介绍了SpringCache抽象注解的使用方式 既然这是一个抽象,我们需要一个具体的缓存存储实现.比价流行的有:基于J ...

  8. springboot整合spring Cache(redis)

    前言 前面有介绍过spring整合redis和redis的一些注意点,在实际开发中,spring cache方便简化的缓存操作的大部分内容.通过注解的方式实现缓存. 阅读前推荐先阅读:redis缓存介 ...

  9. Spring Cache

    在WEB后端应用程序来说,耗时比较大的往往有两个地方:一个是查数据库,一个是调用其它服务的API(因为其它服务最终也要去做查数据库等耗时操作).重复查询也有两种.一种是我们在应用程序中代码写得不好,写 ...

最新文章

  1. TensorFlow Wide And Deep 模型详解与应用 TensorFlow Wide-And-Deep 阅读344 作者简介:汪剑,现在在出门问问负责推荐与个性化。曾在微软雅虎工作,
  2. [概率期望DP]JZOJ 4212 我想大声告诉你
  3. Spring3+ibatis (SQL Server)+pager-taglib.tld查询分页的实现
  4. 虚拟机游戏获取服务器地址,vue获取服务器地址
  5. LeetCode 1124. 表现良好的最长时间段(单调栈/哈希)
  6. Ecilpse常用快捷键
  7. app每秒并发数_性能测试连载 (38) jmeter 线程数与性能测试的负载模式
  8. Android淘宝客链接自动跳转淘宝APP问题
  9. java代码读取txt文件_java读取txt文件代码片段
  10. HTML与CSS如何创建悬停折角纸叠效果
  11. JavaScript---网络编程(8)-DHTML技术演示(1)
  12. Spring Bean生命周期: Bean的实例化
  13. 第2章 第2节-Dijkstra Astar
  14. Hdu 2389 Rain on your Parade
  15. 输入银行卡号,可以自动识别到银行卡类型的方法
  16. 史上最全的 java正则校验 家庭住址 手机号 姓名等
  17. AUC评价指标的原理和实验
  18. 传承新时代浙商精神 天搜科技的做法值得借鉴
  19. linux ss 软件,Linux ss 命令详解
  20. python简单心形代码爱情闪字_《使用Python进行自然语言处理》学习笔记四

热门文章

  1. 推荐免费学习使用的云服务器
  2. 【弹吉他必备的乐理知识】【1】音乐中的音
  3. C++ 求最大公因数
  4. speedoffice(PPT)插入的图片如何自动适合幻灯片页面大小呢?
  5. Ubuntu 10.04 下载(官方地址)
  6. 小程序自定义导航栏指南
  7. 误发邮件如何能撤回?
  8. 关于meta: description HTML 标签的解释 ,SEO
  9. 关于两个蓝牙模块HC-05之间无线通信的AT指令设置
  10. gitlab常用网址