您好,登錄后才能下訂單哦!
這篇文章主要介紹“Spring @Cacheable指定失效時間方法是什么”,在日常操作中,相信很多人在Spring @Cacheable指定失效時間方法是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”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; }
之前對Spring緩存的理解是每次設置緩存之后,重復請求會刷新緩存時間,但是問題排查閱讀源碼發現,跟自己的理解大相徑庭。所有的你以為都僅僅是你以為!!!!
目前項目使用的spring緩存,主要是CacheManager、Cache以及@Cacheable注解,Spring現有的緩存注解無法單獨設置每一個注解的失效時間,Spring官方給的解釋:Spring Cache是一個抽象而不是一個緩存實現方案。
因此對于緩存失效時間(TTL)的策略依賴于底層緩存中間件,官方給舉例:ConcurrentMap是不支持失效時間的,而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; }
因此如果我們需要自行控制緩存失效策略,就可能需要一些開發工作,具體如下。
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的實現方案。
到此,關于“Spring @Cacheable指定失效時間方法是什么”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。