【问题标题】:Spring @Cacheable: Preserve old value on errorSpring @Cacheable:错误时保留旧值
【发布时间】:2015-03-22 23:45:37
【问题描述】:

我打算使用 Spring @Cacheable 注解来缓存调用方法的结果。

但是这个实现对我来说似乎不是很“安全”。据我了解,返回值会被底层缓存引擎缓存,并在调用Spring evict方法时被删除。

我需要一个在加载新值之前不会破坏旧值的实现。这将是必需的,并且以下方案应该有效:

  1. 可缓存方法被调用 -> 返回有效结果
  2. 结果将被 Spring @Cacheable 后端缓存
  3. Spring 使缓存失效,因为它已过期(例如 1 小时的 TTL)
  4. 可缓存方法再次被调用 -> 异常/空值返回!
  5. OLD 结果将再次被缓存,因此,将来对该方法的调用将返回有效结果

这怎么可能?

【问题讨论】:

    标签: java spring caching guava ehcache


    【解决方案1】:

    我在阅读 Spring 代码时可能是错误的,尤其是 org.springframework.cache.interceptor.CacheAspectSupport#execute(org.springframework.cache.interceptor.CacheOperationInvoker, org.springframework.cache.interceptor.CacheAspectSupport.CacheOperationContexts),但我相信抽象并不能提供您真正想要的内容。

    1. Spring 不会使条目过期,这将留给底层缓存实现。
    2. 您提到您希望查看已过期的值。这与我所知道的大多数缓存实现中使用的到期抽象背道而驰。
    3. 在调用错误时返回先前缓存的值显然是特定于用例的。 Spring 抽象只会将错误返回给用户。 CacheErrorHandler 机制只处理缓存调用相关的异常。

    总而言之,在我看来,您所要求的是非常具体的用例,因此不是抽象将/应该提供的东西。

    【讨论】:

      【解决方案2】:

      如果@Cacheable 方法抛出异常,您对提供旧值的要求可以通过对 Google Guava 的最小扩展轻松实现。

      使用以下示例配置

      @Configuration
      @EnableWebMvc
      @EnableCaching
      @ComponentScan("com.yonosoft.poc.cache")
      public class ApplicationConfig extends CachingConfigurerSupport {
          @Bean
          @Override
          public CacheManager cacheManager() {
              SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
      
              GuavaCache todoCache = new GuavaCache("todo", CacheBuilder.newBuilder()
                  .refreshAfterWrite(10, TimeUnit.MINUTES)
                  .maximumSize(10)
                  .build(new CacheLoader<Object, Object>() {
                      @Override
                      public Object load(Object key) throws Exception {
                          CacheKey cacheKey = (CacheKey)key;
                          return cacheKey.method.invoke(cacheKey.target, cacheKey.params);
                      }
                  }));
      
              simpleCacheManager.setCaches(Arrays.asList(todoCache));
      
              return simpleCacheManager;
          }
      
          @Bean
          @Override
          public KeyGenerator keyGenerator() {
              return new KeyGenerator() {
                  @Override
                  public Object generate(Object target, Method method, Object... params) {
                      return new CacheKey(target, method, params);
                  }
              };
          }
      
          private class CacheKey extends SimpleKey {
              private static final long serialVersionUID = -1013132832917334168L;
              private Object target;
              private Method method;
              private Object[] params;
      
              private CacheKey(Object target, Method method, Object... params) {
                  super(params);
                  this.target = target;
                  this.method = method;
                  this.params = params;
              }
          }
      }
      

      CacheKey 的唯一目的是公开SimpleKey 属性。 Guavas refreshAfterWrite 将配置刷新时间而不使缓存条目过期。如果带有@Cacheable 注释的方法抛出异常,缓存将继续提供旧值,直到由于maximumSize 而被驱逐或被成功方法响应的新值替换。您可以将refreshAfterWriteexpireAfterAccessexpireAfterAccess 结合使用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-03-08
        • 1970-01-01
        • 1970-01-01
        • 2016-03-11
        • 2019-05-17
        • 1970-01-01
        • 2012-12-31
        相关资源
        最近更新 更多