经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring » 查看文章
Spring?@Cacheable指定失效时间实例
来源:jb51  时间:2021/12/24 8:46:19  对本文有异议

Spring @Cacheable指定失效时间

新版本配置

  1. @Configuration
  2. @EnableCaching
  3. public class RedisCacheConfig {
  4. @Bean
  5. public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
  6. return (builder) -> {
  7. for (Map.Entry<String, Duration> entry : RedisCacheName.getCacheMap().entrySet()) {
  8. builder.withCacheConfiguration(entry.getKey(),
  9. RedisCacheConfiguration.defaultCacheConfig().entryTtl(entry.getValue()));
  10. }
  11. };
  12. }
  13. public static class RedisCacheName {
  14. public static final String CACHE_10MIN = "CACHE_10MIN";
  15. @Getter
  16. private static final Map<String, Duration> cacheMap;
  17. static {
  18. cacheMap = ImmutableMap.<String, Duration>builder().put(CACHE_10MIN, Duration.ofSeconds(10L)).build();
  19. }
  20. }
  21. }

老版本配置

  1. interface CacheNames{
  2. String CACHE_15MINS = "sssss:cache:15m";
  3. /** 30分钟缓存组 */
  4. String CACHE_30MINS = "sssss:cache:30m";
  5. /** 60分钟缓存组 */
  6. String CACHE_60MINS = "sssss:cache:60m";
  7. /** 180分钟缓存组 */
  8. String CACHE_180MINS = "sssss:cache:180m";
  9. }
  10. @Component
  11. public class RedisCacheCustomizer
  12. implements CacheManagerCustomizer<RedisCacheManager> {
  13. /** CacheManager缓存自定义初始化比较早,尽量不要@autowired 其他spring 组件 */
  14. @Override
  15. public void customize(RedisCacheManager cacheManager) {
  16. // 自定义缓存名对应的过期时间
  17. Map<String, Long> expires = ImmutableMap.<String, Long>builder()
  18. .put(CacheNames.CACHE_15MINS, TimeUnit.MINUTES.toSeconds(15))
  19. .put(CacheNames.CACHE_30MINS, TimeUnit.MINUTES.toSeconds(30))
  20. .put(CacheNames.CACHE_60MINS, TimeUnit.MINUTES.toSeconds(60))
  21. .put(CacheNames.CACHE_180MINS, TimeUnit.MINUTES.toSeconds(180)).build();
  22. // spring cache是根据cache name查找缓存过期时长的,如果找不到,则使用默认值
  23. cacheManager.setDefaultExpiration(TimeUnit.MINUTES.toSeconds(30));
  24. cacheManager.setExpires(expires);
  25. }
  26. }
  27. @Cacheable(key = "key", cacheNames = CacheNames.CACHE_15MINS)
  28. public String demo2(String key) {
  29. return "abc" + key;
  30. }

@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)

  1. private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {
  2. // Special handling of synchronized invocation
  3. if (contexts.isSynchronized()) {
  4. CacheOperationContext context = contexts.get(CacheableOperation.class).iterator().next();
  5. if (isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) {
  6. Object key = generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);
  7. Cache cache = context.getCaches().iterator().next();
  8. try {
  9. return wrapCacheValue(method, cache.get(key, () -> unwrapReturnValue(invokeOperation(invoker))));
  10. }
  11. catch (Cache.ValueRetrievalException ex) {
  12. // The invoker wraps any Throwable in a ThrowableWrapper instance so we
  13. // can just make sure that one bubbles up the stack.
  14. throw (CacheOperationInvoker.ThrowableWrapper) ex.getCause();
  15. }
  16. }
  17. else {
  18. // No caching required, only call the underlying method
  19. return invokeOperation(invoker);
  20. }
  21. }
  22. // Process any early evictions
  23. processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
  24. CacheOperationExpressionEvaluator.NO_RESULT);
  25. // Check if we have a cached item matching the conditions
  26. Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));
  27. // Collect puts from any @Cacheable miss, if no cached item is found
  28. List<CachePutRequest> cachePutRequests = new LinkedList<>();
  29. if (cacheHit == null) {
  30. collectPutRequests(contexts.get(CacheableOperation.class),
  31. CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
  32. }
  33. Object cacheValue;
  34. Object returnValue;
  35. if (cacheHit != null && !hasCachePut(contexts)) {
  36. // If there are no put requests, just use the cache hit
  37. cacheValue = cacheHit.get();
  38. returnValue = wrapCacheValue(method, cacheValue);
  39. }
  40. else {
  41. // Invoke the method if we don't have a cache hit
  42. returnValue = invokeOperation(invoker);
  43. cacheValue = unwrapReturnValue(returnValue);
  44. }
  45. // Collect any explicit @CachePuts
  46. collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);
  47. // Process any collected put requests, either from @CachePut or a @Cacheable miss
  48. for (CachePutRequest cachePutRequest : cachePutRequests) {
  49. cachePutRequest.apply(cacheValue);
  50. }
  51. // Process any late evictions
  52. processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);
  53. return returnValue;
  54. }

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

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的实现方案。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持w3xue。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号