Spring @Cacheable指定失效时间实例

作者:tony乐天 时间:2022-12-06 08:58:35 

Spring @Cacheable指定失效时间

新版本配置


@Configuration
@EnableCaching
public class RedisCacheConfig {
   @Bean
   public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
       return (builder) -> {
           for (Map.Entry<String, Duration> entry : RedisCacheName.getCacheMap().entrySet()) {
               builder.withCacheConfiguration(entry.getKey(),
                       RedisCacheConfiguration.defaultCacheConfig().entryTtl(entry.getValue()));
           }
       };
   }

public static class RedisCacheName {
       public static final String CACHE_10MIN = "CACHE_10MIN";
       @Getter
       private static final Map<String, Duration> cacheMap;
       static {
           cacheMap = ImmutableMap.<String, Duration>builder().put(CACHE_10MIN, Duration.ofSeconds(10L)).build();
       }
   }

}

老版本配置


interface CacheNames{
   String CACHE_15MINS = "sssss:cache:15m";
       /** 30分钟缓存组 */
   String CACHE_30MINS = "sssss:cache:30m";
       /** 60分钟缓存组 */
   String CACHE_60MINS = "sssss:cache:60m";
       /** 180分钟缓存组 */
   String CACHE_180MINS = "sssss:cache:180m";
}

@Component
public class RedisCacheCustomizer
           implements CacheManagerCustomizer<RedisCacheManager> {
       /** CacheManager缓存自定义初始化比较早,尽量不要@autowired 其他spring 组件 */
       @Override
       public void customize(RedisCacheManager cacheManager) {
           // 自定义缓存名对应的过期时间
           Map<String, Long> expires = ImmutableMap.<String, Long>builder()
                   .put(CacheNames.CACHE_15MINS, TimeUnit.MINUTES.toSeconds(15))
                   .put(CacheNames.CACHE_30MINS, TimeUnit.MINUTES.toSeconds(30))
                   .put(CacheNames.CACHE_60MINS, TimeUnit.MINUTES.toSeconds(60))
                   .put(CacheNames.CACHE_180MINS, TimeUnit.MINUTES.toSeconds(180)).build();
           // spring cache是根据cache name查找缓存过期时长的,如果找不到,则使用默认值
           cacheManager.setDefaultExpiration(TimeUnit.MINUTES.toSeconds(30));
           cacheManager.setExpires(expires);
       }
   }

@Cacheable(key = "key", cacheNames = CacheNames.CACHE_15MINS)
   public String demo2(String key) {
       return "abc" + key;
 }

@Cacheable缓存失效时间策略默认实现及扩展

之前对Spring缓存的理解是每次设置缓存之后,重复请求会刷新缓存时间,但是问题排查阅读源码发现,跟自己的理解大相径庭。所有的你以为都仅仅是你以为!!!!

背景

目前项目使用的spring缓存,主要是CacheManager、Cache以及@Cacheable注解,Spring现有的缓存注解无法单独设置每一个注解的失效时间,Spring官方给的解释:Spring Cache是一个抽象而不是一个缓存实现方案。

因此对于缓存失效时间(TTL)的策略依赖于底层缓存中间件,官方给举例:ConcurrentMap是不支持失效时间的,而Redis是支持失效时间的。

Spring @Cacheable指定失效时间实例

Spring Cache Redis实现

Spring缓存注解@Cacheable底层的CacheManager与Cache如果使用Redis方案的话,首次设置缓存数据之后,每次重复请求相同方法读取缓存并不会刷新失效时间,这是Spring的默认行为(受一些缓存影响,一直以为每次读缓存也会刷新缓存失效时间)。

可以参见源码:

org.springframework.cache.interceptor.CacheAspectSupport#execute(org.springframework.cache.interceptor.CacheOperationInvoker, java.lang.reflect.Method, org.springframework.cache.interceptor.CacheAspectSupport.CacheOperationContexts)


private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {
// Special handling of synchronized invocation
if (contexts.isSynchronized()) {
CacheOperationContext context = contexts.get(CacheableOperation.class).iterator().next();
if (isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) {
Object key = generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);
Cache cache = context.getCaches().iterator().next();
try {
return wrapCacheValue(method, cache.get(key, () -> unwrapReturnValue(invokeOperation(invoker))));
}
catch (Cache.ValueRetrievalException ex) {
// The invoker wraps any Throwable in a ThrowableWrapper instance so we
// can just make sure that one bubbles up the stack.
throw (CacheOperationInvoker.ThrowableWrapper) ex.getCause();
}
}
else {
// No caching required, only call the underlying method
return invokeOperation(invoker);
}
}

// Process any early evictions
processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
CacheOperationExpressionEvaluator.NO_RESULT);

// Check if we have a cached item matching the conditions
Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));

// Collect puts from any @Cacheable miss, if no cached item is found
List<CachePutRequest> cachePutRequests = new LinkedList<>();
if (cacheHit == null) {
collectPutRequests(contexts.get(CacheableOperation.class),
CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
}
Object cacheValue;
Object returnValue;

if (cacheHit != null && !hasCachePut(contexts)) {
// If there are no put requests, just use the cache hit
cacheValue = cacheHit.get();
returnValue = wrapCacheValue(method, cacheValue);
}
else {
// Invoke the method if we don't have a cache hit
returnValue = invokeOperation(invoker);
cacheValue = unwrapReturnValue(returnValue);
}

// Collect any explicit @CachePuts
collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);

// Process any collected put requests, either from @CachePut or a @Cacheable miss
for (CachePutRequest cachePutRequest : cachePutRequests) {
cachePutRequest.apply(cacheValue);
}

// Process any late evictions
processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);
return returnValue;
}

因此如果我们需要自行控制缓存失效策略,就可能需要一些开发工作,具体如下。

Spring Cache 失效时间自行刷新

1:基于Spring的Cache组件进行定制,对get方法进行重写,刷新过期时间。相对简单,不难;此处不贴代码了。

2:可以使用后台线程进行定时的缓存刷新,以达到刷新时间的作用。

3:使用spring data redis模块,该模块提供对了TTL更新策略的,可以参见:org.springframework.data.redis.core.PartialUpdate

注意:

Spring对于@Cacheable注解是由spring-context提供的,spring-context提供的缓存的抽象,是一套标准而不是实现。

而PartialUpdate是由于spring-data-redis提供的,spring-data-redis是一套spring关于redis的实现方案。

来源:https://blog.csdn.net/u013269532/article/details/105014342

标签:Spring,@Cacheable,失效时间
0
投稿

猜你喜欢

  • spring与mybatis三种整合方法

    2021-11-27 22:00:46
  • Java多维数组和Arrays类方法总结详解

    2022-04-27 22:30:18
  • 浅谈C#单例模式的实现和性能对比

    2022-10-29 10:46:30
  • SpringCloud学习笔记之SpringCloud搭建父工程的过程图解

    2022-10-28 02:38:30
  • 获取Android签名证书的公钥和私钥的简单实例

    2022-10-02 22:20:13
  • mybatis日志打印的两款IDEA插件推荐

    2022-01-12 07:55:52
  • Android自定义View新年烟花、祝福语横幅动画

    2022-01-24 21:31:27
  • c# Invoke和BeginInvoke 区别分析

    2022-09-03 15:20:49
  • java开发分布式服务框架Dubbo原理机制详解

    2023-01-04 19:53:01
  • c#滚动字幕动画窗体制作步骤

    2023-12-06 20:12:58
  • 详解Java对象结构与对象锁的升级

    2021-12-05 16:18:38
  • Gson之toJson和fromJson方法的具体使用

    2021-07-20 16:28:47
  • Java绘制迷宫动画并显示的示例代码

    2022-04-06 22:37:45
  • Java C++题解leetcode字符串轮转KMP算法详解

    2023-05-30 11:28:49
  • C#实现数独解法

    2022-10-25 18:22:43
  • Andriod 获取电池的信息实例代码

    2022-01-01 07:17:00
  • Spring Security+JWT简述(附源码)

    2022-10-16 11:57:51
  • C#实现对文件进行加密解密的方法

    2023-05-28 14:02:44
  • 浅谈Android 指纹解锁技术

    2023-06-04 18:59:06
  • java实现简单年龄计算器

    2022-01-28 02:23:34
  • asp之家 软件编程 m.aspxhome.com