0


spring cache ttl 过期

spring cache ttl 过期实现

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

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

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

@BeanpublicCacheManagercacheManager(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序列化器returnRedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(lettuceConnectionFactory)).cacheDefaults(redisCacheConfiguration).build();}

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

实现

思路:

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

定义注解 @TTLCacheable

继承 @Cacheable 注解

@Target({ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Inherited@Cacheable@Documentedpublic@interfaceTTLCacheable{@AliasFor(annotation =Cacheable.class, value ="value")String[]value()default{};@AliasFor(annotation =Cacheable.class, value ="cacheNames")String[]cacheNames()default{};@AliasFor(annotation =Cacheable.class, value ="key")Stringkey()default"";@AliasFor(annotation =Cacheable.class, value ="keyGenerator")StringkeyGenerator()default"";@AliasFor(annotation =Cacheable.class, value ="cacheResolver")StringcacheResolver()default"";@AliasFor(annotation =Cacheable.class, value ="condition")Stringcondition()default"";@AliasFor(annotation =Cacheable.class, value ="unless")Stringunless()default"";@AliasFor(annotation =Cacheable.class, value ="sync")booleansync()defaultfalse;/**
     * cache 过期时间
     * @return
     */intttl()default0;}

刷新ttl时间 TTLCachePostProcessor

publicclassTTLCachePostProcessorimplementsBeanPostProcessor,SmartInitializingSingleton,BeanFactoryAware{privateBeanFactory beanFactory;privatestaticSet<TTLCacheable> cacheables =newConcurrentHashSet<>();privateRedisTTLCacheConfig redisTTLCacheConfig;publicTTLCachePostProcessor(RedisTTLCacheConfig redisTTLCacheConfig){this.redisTTLCacheConfig = redisTTLCacheConfig;}@OverridepublicvoidafterSingletonsInstantiated(){if(!CollectionUtils.isEmpty(cacheables)){CacheManager cm = beanFactory.getBean(CacheManager.class);if(!(cm instanceofRedisCacheManager)){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 =newHashSet<>();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();}}@OverridepublicObjectpostProcessBeforeInitialization(Object bean,String beanName)throwsBeansException{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;}@OverridepublicvoidsetBeanFactory(BeanFactory beanFactory)throwsBeansException{this.beanFactory = beanFactory;}}

RedisTTLCacheConfig:

@DatapublicclassRedisTTLCacheConfig{/**
     * Entry expiration. By default the entries never expire.
     */privateDuration timeToLive;/**
     * Allow caching null values.
     */privateboolean cacheNullValues =true;/**
     * Key prefix.
     */privateString keyPrefix;/**
     * Whether to use the key prefix when writing to Redis.
     */privateboolean useKeyPrefix =true;privateConversionService conversionService;/**
     * 序列化
     */privateRedisSerializationContext.SerializationPair<String> keySerializationPair;privateRedisSerializationContext.SerializationPair<Object> valueSerializationPair;}

自动装配

  • @EnableCacheTTLOperation
  • CacheTTLAutoConfiguration
  • CacheTTLOperationConfigSelector
@EnableCacheTTLOperation
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(CacheTTLOperationConfigSelector.class)public@interfaceEnableCacheTTLOperation{}
CacheTTLAutoConfiguration
@Configuration@ConditionalOnClass(value ={RedisTemplate.class,CacheInterceptor.class})@ConditionalOnBean(RedisTemplate.class)@EnableConfigurationProperties(value ={CacheProperties.class})publicclassCacheTTLAutoConfiguration{@Bean@ConditionalOnMissingBean(RedisTTLCacheConfig.class)publicRedisTTLCacheConfigredisTTLCacheConfig(CacheProperties properties,RedisTemplate redisTemplate){CacheProperties.Redis redisProperties = properties.getRedis();RedisTTLCacheConfig config =newRedisTTLCacheConfig();
        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;}@BeanpublicTTLCachePostProcessorttlCachePostProcessor(ObjectProvider<RedisTTLCacheConfig> provider){returnnewTTLCachePostProcessor(provider.getIfAvailable());}}
CacheTTLOperationConfigSelector
publicclassCacheTTLOperationConfigSelectorimplementsImportSelector{@OverridepublicString[]selectImports(AnnotationMetadata importingClassMetadata){returnnewString[]{"com.x.x.CacheTTLAutoConfiguration"};}}

测试

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

Controller

@Slf4j@RestControllerpublicclassCacheController{@TTLCacheable(cacheNames ="product", key ="#id", ttl =120)@GetMapping("/product/{id}")publicStringgetById(@PathVariable("id")Integer id){
        log.info("get from db");return"success";}@TTLCacheable(cacheNames ="product2", key ="#id")@GetMapping("/product2/{id}")publicStringgetById2(@PathVariable("id")Integer id){
        log.info("get from db");return"success";}@Cacheable(cacheNames ="product3", key ="#id")@GetMapping("/product3/{id}")publicStringgetById3(@PathVariable("id")Integer id){
        log.info("get from db");return"success";}}

good luck!

标签: cache

本文转载自: https://blog.csdn.net/u013887008/article/details/127896948
版权归原作者 iiaythi 所有, 如有侵权,请联系我们删除。

“spring cache ttl 过期”的评论:

还没有评论