spring cache ttl 过期
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 过期相关推荐
- 玩转Spring Cache --- 整合进程缓存之王Caffeine Cache和Ehcache3.x【享学Spring】
每篇一句 人到中年就是一部西游记:悟空的压力,八戒的身材,沙僧的发型,唐僧的唠叨 前言 前面文章大篇幅详细讲解了Spring Cache缓存抽象.三大缓存注解的工作原理等等.若是细心的小伙伴会发现:讲 ...
- Spring Cache使用Redis自定义缓存key
一.什么是Spring Cache 从spring 3.1版本开始,提供了一种透明的方式来为现有的spring 应用添加cache.在应用层面与后端存储之间,提供了一层抽象,这层抽象目的在于封装各种可 ...
- redis,Spring Cache,Mango,ES
目录 Redis Redis下载与安装 Redis常用命令 在Java中操作Redis Jedis Spring Data Redis Spring Cache Spring Cache介绍 Spri ...
- 22-08-06 西安 尚医通(03)EasyExcel; Spring Cache 、Redis做缓存
EasyExcel EasyExcel:一行一行读取到内存 EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单.节省内存著称 POI:java里操作excel,读取.创建excel ...
- Spring Cache的基本使用
文章目录 一.概述 二.Spring Cache 的使用 2.1 环境搭建 2.2 缓存的读模式 @Cacheable 2.3 自定义缓存配置 2.4 @CachePut 2.5 @CacheEvic ...
- spring cache基础篇
Spring从3.1开始定义了org.springframework.cache.Cache和 org.springframework.cache.CacheManager接口来统一不同的缓存技术:并 ...
- Spring Cache抽象-使用Java类注解的方式整合EhCache
概述 工程结构 源码 概述 Spring Cache抽象-之缓存注解这篇博文中我们介绍了SpringCache抽象注解的使用方式 既然这是一个抽象,我们需要一个具体的缓存存储实现.比价流行的有:基于J ...
- springboot整合spring Cache(redis)
前言 前面有介绍过spring整合redis和redis的一些注意点,在实际开发中,spring cache方便简化的缓存操作的大部分内容.通过注解的方式实现缓存. 阅读前推荐先阅读:redis缓存介 ...
- Spring Cache
在WEB后端应用程序来说,耗时比较大的往往有两个地方:一个是查数据库,一个是调用其它服务的API(因为其它服务最终也要去做查数据库等耗时操作).重复查询也有两种.一种是我们在应用程序中代码写得不好,写 ...
最新文章
- TensorFlow Wide And Deep 模型详解与应用 TensorFlow Wide-And-Deep 阅读344 作者简介:汪剑,现在在出门问问负责推荐与个性化。曾在微软雅虎工作,
- [概率期望DP]JZOJ 4212 我想大声告诉你
- Spring3+ibatis (SQL Server)+pager-taglib.tld查询分页的实现
- 虚拟机游戏获取服务器地址,vue获取服务器地址
- LeetCode 1124. 表现良好的最长时间段(单调栈/哈希)
- Ecilpse常用快捷键
- app每秒并发数_性能测试连载 (38) jmeter 线程数与性能测试的负载模式
- Android淘宝客链接自动跳转淘宝APP问题
- java代码读取txt文件_java读取txt文件代码片段
- HTML与CSS如何创建悬停折角纸叠效果
- JavaScript---网络编程(8)-DHTML技术演示(1)
- Spring Bean生命周期: Bean的实例化
- 第2章 第2节-Dijkstra Astar
- Hdu 2389 Rain on your Parade
- 输入银行卡号,可以自动识别到银行卡类型的方法
- 史上最全的 java正则校验 家庭住址 手机号 姓名等
- AUC评价指标的原理和实验
- 传承新时代浙商精神 天搜科技的做法值得借鉴
- linux ss 软件,Linux ss 命令详解
- python简单心形代码爱情闪字_《使用Python进行自然语言处理》学习笔记四