Spring @Cacheable:出错时保留旧值

tho*_*ser 7 java spring caching ehcache guava

我计划使用 Spring @Cacheable 注释来缓存调用方法的结果。

但这种实现对我来说看起来不太“安全”。据我了解,返回值会被底层缓存引擎缓存,在调用Spring evict方法时会被删除。

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

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

这怎么可能呢?

小智 3

@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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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