我如何告诉 Spring 缓存不要在 @Cacheable 注释中缓存空值
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12113725/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I tell Spring cache not to cache null value in @Cacheable annotation
提问by David Zhao
Is there a way to specify that if the method returns null value, then don't cache the result in @Cacheable annotation for a method like this?
有没有办法指定如果该方法返回空值,则不要将结果缓存在@Cacheable 注释中以用于这样的方法?
@Cacheable(value="defaultCache", key="#pk")
public Person findPerson(int pk) {
return getSession.getPerson(pk);
}
Update: here is the JIRA issue submitted regarding caching null value last November, which hasn't resolved yet: [#SPR-8871] @Cachable condition should allow referencing return value - Spring Projects Issue Tracker
更新:这是去年 11 月提交的关于缓存空值的 JIRA 问题,尚未解决: [#SPR-8871] @Cachable 条件应该允许引用返回值 - Spring Projects Issue Tracker
回答by TechTrip
Hooray, as of Spring 3.2 the framework allows for this using Spring SPEL and unless. Note from the java doc surrounding Cacheable:
万岁,从 Spring 3.2 开始,框架允许使用 Spring SPEL 和unless. 围绕 Cacheable 的 java doc 中的注释:
public abstract String unless
Spring Expression Language (SpEL) attribute used to veto method caching.
Unlike condition(), this expression is evaluated after the method has been called and can therefore refer to the result. Default is "", meaning that caching is never vetoed.
公共抽象字符串,除非
Spring 表达式语言 (SpEL) 属性用于否决方法缓存。
与 condition() 不同,这个表达式是在调用方法之后计算的,因此可以引用结果。默认为 "",表示永远不会否决缓存。
The important aspect is that unlessis evaluated after the method has been called. This makes perfect sense because the method will never get executed if the key is already in the cache.
重要的方面是unless在调用方法后进行评估。这是完全合理的,因为如果密钥已经在缓存中,该方法将永远不会被执行。
So in the above example you would simply annotate as follows (#result is available to test the return value of a method):
因此,在上面的示例中,您只需注释如下(#result 可用于测试方法的返回值):
@Cacheable(value="defaultCache", key="#pk", unless="#result == null")
public Person findPerson(int pk) {
return getSession.getPerson(pk);
}
I would imagine this condition arises from the use of pluggable cache implementations such as Ehcache which allows caching of nulls. Depending on your use case scenario this may or may not be desirable.
我想这种情况是由于使用可插拔缓存实现(例如允许缓存空值的 Ehcache)引起的。根据您的用例场景,这可能是也可能不是可取的。
回答by Boris Treukhov
updatethis answer is outdated now, for Spring 3.2 and later see Tech Trip's answer, OP: feel free to mark it as accepted.
更新此答案现已过时,对于 Spring 3.2 及更高版本,请参阅 Tech Trip 的答案,OP:随时将其标记为已接受。
I don't think that it's possible(even though there's conditional Cache eviction in Spring that can be executed after the method invocation with @CacheEvictparameter beforeInvocationset to false, which is default value) examining the CacheAspectSupportclass shows that the returned value is not stored anywhere before the inspectAfterCacheEvicts(ops.get(EVICT));call.
我认为这是不可能的(即使 Spring 中有条件缓存驱逐可以在方法调用后执行,@CacheEvict参数beforeInvocation设置为 false,这是默认值)检查CacheAspectSupport类显示返回的值没有存储在任何地方之前的inspectAfterCacheEvicts(ops.get(EVICT));通话。
protected Object execute(Invoker invoker, Object target, Method method, Object[] args) {
// check whether aspect is enabled
// to cope with cases where the AJ is pulled in automatically
if (!this.initialized) {
return invoker.invoke();
}
// get backing class
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(target);
if (targetClass == null && target != null) {
targetClass = target.getClass();
}
final Collection<CacheOperation> cacheOp = getCacheOperationSource().getCacheOperations(method, targetClass);
// analyze caching information
if (!CollectionUtils.isEmpty(cacheOp)) {
Map<String, Collection<CacheOperationContext>> ops = createOperationContext(cacheOp, method, args, target, targetClass);
// start with evictions
inspectBeforeCacheEvicts(ops.get(EVICT));
// follow up with cacheable
CacheStatus status = inspectCacheables(ops.get(CACHEABLE));
Object retVal = null;
Map<CacheOperationContext, Object> updates = inspectCacheUpdates(ops.get(UPDATE));
if (status != null) {
if (status.updateRequired) {
updates.putAll(status.cUpdates);
}
// return cached object
else {
return status.retVal;
}
}
retVal = invoker.invoke();
inspectAfterCacheEvicts(ops.get(EVICT));
if (!updates.isEmpty()) {
update(updates, retVal);
}
return retVal;
}
return invoker.invoke();
}
回答by Lex
If Spring annotation
如果 Spring 注解
@Cacheable(value="defaultCache", key="#pk",unless="#result!=null")
does not work ,you can try:
不起作用,您可以尝试:
@CachePut(value="defaultCache", key="#pk",unless="#result==null")
It works for me.
这个对我有用。

