【发布时间】:2015-12-19 11:20:46
【问题描述】:
当我在redis中使用spring缓存时,我在两个应用程序中使用它,一个读写,另一个只读,我该如何配置?
我尝试这样做,但它不起作用!
@Cacheable(value = "books", key = "#isbn", condition = "false")
谁能帮忙?
【问题讨论】:
标签: spring-cache
当我在redis中使用spring缓存时,我在两个应用程序中使用它,一个读写,另一个只读,我该如何配置?
我尝试这样做,但它不起作用!
@Cacheable(value = "books", key = "#isbn", condition = "false")
谁能帮忙?
【问题讨论】:
标签: spring-cache
您误解了@Cacheable 注释的“condition”属性的用途。根据documentation...
如果为真,该方法被缓存 - 如果不是,它的行为就像该方法是 未缓存,无论值是什么,每次都执行 是否在缓存中或使用了哪些参数。
condition 属性只是确定在执行(可能很昂贵)方法之前是否先查询缓存(例如 Redis)。如果condition 的计算结果为 false,则该方法将始终被执行并且结果随后被缓存。
在只读应用程序中,我假设您希望先查询缓存,如果值不在缓存中,则执行该方法,但是,不要缓存结果。这是正确的吗?
如果是这样,那么您只需要指定unless 属性,而不是像这样指定condition 属性...
@Cacheable(value="books", key="#isbn", unless="true")
void someBookMutatingOperation(String isbn, ...) { .. }
但是,如果您想完全避免在只读(版本)应用程序中调用可缓存方法,而不管缓存中是否实际存在值,都只查询缓存,那么您的问题就很严重了有点复杂/困难。
Spring 的缓存抽象操作的前提是,如果一个值不在缓存中,则返回 null 表示缓存未命中,然后进行后续方法调用。只有当缓存返回指定键的值时,才会避免方法调用。
如果没有自定义扩展(可能使用(额外的)AOP 拦截器),就无法避免 OOTB 行为。
除非您的用例需要,否则我不会详细说明后面的技术。
希望这会有所帮助。
【讨论】:
unless = "true"。
@托尼·宾
首先,很抱歉在我之前的回答中误导了您...
如果条件评估为 false,则该方法将始终为 执行并随后缓存结果。
最后一部分不正确。事实上,condition 属性确实 阻止了 @Cacheable 方法结果被缓存。但是,condition 和 unless 属性都不会阻止调用 @Cacheable 服务方法。
另外,我上面的代码示例不正确。 unless 属性需要设置为 true 以防止缓存 @Cacheable 方法结果。
在重读 Spring 参考指南中的this section 之后,我意识到自己的错误并写了一个example test class 来验证 Spring 的“条件”缓存行为。
所以...
关于您的业务用例,我根据您的原始问题理解它的方式以及随后您对我之前回答的回复,您有一个 @Cacheable 服务方法,需要在阅读中禁止调用 -仅应用程序,无论该值是否在缓存中!换句话说,应该始终从缓存中检索该值,并且应该不以只读模式调用 @Cacheable 服务方法。
现在要避免使用 Spring 基础架构组件引用污染您的应用程序代码,具体来说,使用 Spring CacheManager,这是一个很好的“交叉-切割关注点”(因为可能存在多个基于变异的应用程序服务操作),因此可以使用 AOP 适当地处理。
我已经编写了一个满足您要求的示例here。
这是一个独立的测试类。该测试类的主要特征包括...
使用外部配置(通过app.mode.read-only系统属性)来确定应用程序是否处于只读模式。
使用 AOP 和自定义 Aspect 来控制是否允许后续调用 Joint Point(即 @Cacheable 服务方法)(否,在只读上下文中)。此外,我根据优先级适当地设置了 Advice(即基于 @Cacheable 的建议以及 UseCacheExclusivelyInReadOnlyModeAspect 方面中的 handleReadOnlyMode 建议)应该触发的顺序。
注意服务方法上的@Cacheable注解...
@Cacheable(value = "Factorials", unless = "T(java.lang.System).getProperty('app.mode.read-only', 'false')")
public Long factorial(long number) { .. }
您可以通过测试类中的System.err 输出语句查看预期行为。
希望这会有所帮助!
【讨论】:
@约翰布鲁姆 谢谢!新年快乐。 您的回答启发了我,我已经阅读了部分 spring 缓存源代码。 CacheInterceptor 类。 CacheAspectSupport 类。
private Object execute(CacheOperationInvoker invoker, CacheOperationContexts contexts) {
// Process any early evictions
processCacheEvicts(contexts.get(CacheEvictOperation.class), true, ExpressionEvaluator.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<CachePutRequest>();
if (cacheHit == null) {
collectPutRequests(contexts.get(CacheableOperation.class), ExpressionEvaluator.NO_RESULT, cachePutRequests);
}
Cache.ValueWrapper result = null;
// If there are no put requests, just use the cache hit
if (cachePutRequests.isEmpty() && !hasCachePut(contexts)) {
result = cacheHit;
}
// Invoke the method if don't have a cache hit
if (result == null) {
result = new SimpleValueWrapper(invokeOperation(invoker));
}
// Collect any explicit @CachePuts
collectPutRequests(contexts.get(CachePutOperation.class), result.get(), cachePutRequests);
// Process any collected put requests, either from @CachePut or a @Cacheable miss
for (CachePutRequest cachePutRequest : cachePutRequests) {
cachePutRequest.apply(result.get());
}
// Process any late evictions
processCacheEvicts(contexts.get(CacheEvictOperation.class), false, result.get());
return result.get();
}
我认为应该阻止 cachePutRequest 执行。如果没有命中缓存,则调用@Cacheable 的方法体并且不缓存结果。使用除非将阻止方法调用。这是正确的吗?
【讨论】:
unless”。不过,该值应设置为 true。我更正了上面的原始/第一个答案。干杯。