如何告诉Spring缓存不要在@Cacheable注解中缓存空值

问题描述 投票:0回答:3

有没有办法指定如果方法返回 null 值,则不要将结果缓存在此类方法的 @Cacheable 注解中?

@Cacheable(value="defaultCache", key="#pk")
public Person findPerson(int pk) {
   return getSession.getPerson(pk);
}

更新: 这是去年 11 月提交的有关缓存 null 值的 JIRA 问题,但尚未解决: [#SPR-8871] @Cachable 条件应该允许引用返回值 - Spring 项目问题跟踪器

spring caching memcached
3个回答
182
投票

万岁,从 Spring 3.2 开始,框架允许使用 Spring SpEL 和

unless
实现这一点。来自
Cacheable
元素
unless
的 Java 文档注释:

Spring 表达式语言 (SpEL) 表达式用于否决方法缓存。如果条件评估为 true,则否决缓存结果。

与condition()不同,该表达式在调用方法后进行计算,因此可以引用结果。默认值为“”,这意味着缓存永远不会被否决。

重要的一点是

unless
在调用该方法后进行评估。这是完全有道理的,因为如果密钥已经在缓存中,该方法将永远不会被执行。

所以在上面的例子中你只需注释如下(#result可用于测试方法的返回值):

@Cacheable(value="defaultCache", key="#pk", unless="#result == null")
public Person findPerson(int pk) {
   return getSession.getPerson(pk);
}

我认为这种情况是由于使用可插入缓存实现(例如允许缓存空值的 Ehcache)而引起的。根据您的用例场景,这可能是理想的,也可能不是。


7
投票

更新这个答案现在已经过时了,对于Spring 3.2及更高版本,请参阅Tech Trip的答案,OP:随意将其标记为已接受。

我认为这是不可能的(尽管 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();
}

5
投票

如果Spring注解

@Cacheable(value="defaultCache", key="#pk", unless="#result==null")

不起作用,你可以尝试:

@CachePut(value="defaultCache", key="#pk", unless="#result==null")

它对我有用。

© www.soinside.com 2019 - 2024. All rights reserved.