Spring @Cacheable:出错时保留旧值
Spring @Cacheable: Preserve old value on error
我打算使用 Spring @Cacheable 注释来缓存调用方法的结果。
但这个实现在我看来并不十分 "safe"。据我了解,returned 值将由底层缓存引擎缓存,并在调用 Spring evict 方法时删除。
我需要一个在加载新值之前不会破坏旧值的实现。这是必需的,以下情况应该有效:
- 调用了可缓存的方法 -> 有效结果 returned
- 结果将由 Spring @Cacheable 后端缓存
- Spring 使缓存失效,因为它已过期(例如 TTL 为 1 小时)
- 再次调用可缓存方法 -> Exception/null 值 returned!
- 旧结果将被再次缓存,因此,该方法的未来调用将return一个有效结果
这怎么可能?
我对 Spring 代码的阅读可能是错误的,特别是 org.springframework.cache.interceptor.CacheAspectSupport#execute(org.springframework.cache.interceptor.CacheOperationInvoker, org.springframework.cache.interceptor.CacheAspectSupport.CacheOperationContexts)
,但我相信抽象并没有提供您所要求的内容。
- Spring不会使条目过期,这将留给底层缓存实现。
- 您提到您希望查看已过期的值。这与我所知道的大多数缓存实现中使用的到期抽象相反。
- 调用错误时返回先前缓存的值显然是特定于用例的。 Spring 抽象只会将错误抛回给用户。
CacheErrorHandler
机制只处理缓存调用相关的异常。
总而言之,在我看来,您所要求的是非常特定于用例的,因此不是抽象 would/should 提供的东西。
如果 @Cacheable
方法抛出异常,您提供旧值的要求可以通过对 Google 番石榴的最小扩展轻松实现。
使用下面的示例配置
@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
被驱逐或被成功方法响应的新值替换。您可以将 refreshAfterWrite
与 expireAfterAccess
和 expireAfterAccess
.
结合使用
我打算使用 Spring @Cacheable 注释来缓存调用方法的结果。
但这个实现在我看来并不十分 "safe"。据我了解,returned 值将由底层缓存引擎缓存,并在调用 Spring evict 方法时删除。
我需要一个在加载新值之前不会破坏旧值的实现。这是必需的,以下情况应该有效:
- 调用了可缓存的方法 -> 有效结果 returned
- 结果将由 Spring @Cacheable 后端缓存
- Spring 使缓存失效,因为它已过期(例如 TTL 为 1 小时)
- 再次调用可缓存方法 -> Exception/null 值 returned!
- 旧结果将被再次缓存,因此,该方法的未来调用将return一个有效结果
这怎么可能?
我对 Spring 代码的阅读可能是错误的,特别是 org.springframework.cache.interceptor.CacheAspectSupport#execute(org.springframework.cache.interceptor.CacheOperationInvoker, org.springframework.cache.interceptor.CacheAspectSupport.CacheOperationContexts)
,但我相信抽象并没有提供您所要求的内容。
- Spring不会使条目过期,这将留给底层缓存实现。
- 您提到您希望查看已过期的值。这与我所知道的大多数缓存实现中使用的到期抽象相反。
- 调用错误时返回先前缓存的值显然是特定于用例的。 Spring 抽象只会将错误抛回给用户。
CacheErrorHandler
机制只处理缓存调用相关的异常。
总而言之,在我看来,您所要求的是非常特定于用例的,因此不是抽象 would/should 提供的东西。
如果 @Cacheable
方法抛出异常,您提供旧值的要求可以通过对 Google 番石榴的最小扩展轻松实现。
使用下面的示例配置
@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
被驱逐或被成功方法响应的新值替换。您可以将 refreshAfterWrite
与 expireAfterAccess
和 expireAfterAccess
.