Spring @Cacheable指定失效时间方法是什么
这篇文章主要介绍“Spring @Cacheable指定失效时间方法是什么”,在日常操作中,相信很多人在Spring @Cacheable指定失效时间方法是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Spring @Cacheable指定失效时间方法是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
Spring @Cacheable指定失效时间
新版本配置
@Configuration@EnableCachingpublic 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"; String CACHE_30MINS = "sssss:cache:30m"; String CACHE_60MINS = "sssss:cache:60m"; String CACHE_180MINS = "sssss:cache:180m";} @Component public class RedisCacheCustomizer implements CacheManagerCustomizer<RedisCacheManager> { @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 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 invocationif (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 methodreturn invokeOperation(invoker);}} // Process any early evictionsprocessCacheEvicts(contexts.get(CacheEvictOperation.class), true,CacheOperationExpressionEvaluator.NO_RESULT); // Check if we have a cached item matching the conditionsCache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class)); // Collect puts from any @Cacheable miss, if no cached item is foundList<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 hitcacheValue = cacheHit.get();returnValue = wrapCacheValue(method, cacheValue);}else {// Invoke the method if we don't have a cache hitreturnValue = invokeOperation(invoker);cacheValue = unwrapReturnValue(returnValue);} // Collect any explicit @CachePutscollectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests); // Process any collected put requests, either from @CachePut or a @Cacheable missfor (CachePutRequest cachePutRequest : cachePutRequests) {cachePutRequest.apply(cacheValue);} // Process any late evictionsprocessCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue); return returnValue;}
因此如果我们需要自行控制缓存失效策略,就可能需要一些开发工作,具体如下。
Spring Cache 失效时间自行刷新
基于Spring的Cache组件进行定制,对get方法进行重写,刷新过期时间。相对简单,不难;此处不贴代码了。
可以使用后台线程进行定时的缓存刷新,以达到刷新时间的作用。
使用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的实现方案。
到此,关于“Spring @Cacheable指定失效时间方法是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341