【问题标题】:How do I tell Spring cache not to cache null value in @Cacheable annotation如何告诉 Spring 缓存不要在 @Cacheable 注释中缓存空值
【发布时间】:2012-08-20 06:33:25
【问题描述】:

有没有办法指定如果方法返回空值,那么对于这样的方法不要将结果缓存在@Cacheable注解中?

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

更新: 这是去年 11 月提交的关于缓存空值的 JIRA 问题,尚未解决: [#SPR-8871] @Cachable condition should allow referencing return value - Spring Projects Issue Tracker

【问题讨论】:

  • 嗨,我认为您应该接受 Tech Trip 的回答,因为它与当前版本的 Spring 更相关。

标签: spring caching memcached


【解决方案1】:

万岁,从 Spring 3.2 开始,框架允许使用 Spring SPEL 和 unless 进行此操作。来自围绕 Cacheable 的 java 文档的注释:

http://static.springsource.org/spring/docs/3.2.x/javadoc-api/org/springframework/cache/annotation/Cacheable.html

公共抽象字符串除非

用于否决方法缓存的 Spring 表达式语言 (SpEL) 属性。

与 condition() 不同,此表达式在方法被调用后进行评估,因此可以引用结果。默认为“”,表示缓存永远不会被否决。

重要的方面是unless 在方法被调用之后被评估。这很有意义,因为如果密钥已经在缓存中,该方法将永远不会被执行。

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

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

我想这种情况是由于使用了可插入的缓存实现,例如允许缓存空值的 Ehcache。根据您的用例场景,这可能是可取的,也可能是不可取的。

【讨论】:

  • 您好,我有一个场景,redis缓存服务器缓存异常。我必须避免将异常缓存到服务器。我可以为此使用“除非”参数吗?是否可以在除非中提及例外情况?请分享您的建议。提前致谢
  • JSR107 提供了一种机制来始终调用带注释的方法并仍然通过 cacheResult#skipGet 缓存结果。根据 skipGet 上的 API,如果为 true,则也会跳过对抛出异常的调用前检查。但是,如果在调用过程中抛出异常,它将按照标准异常缓存规则进行缓存,因此可能无法满足您的需求。然而,使用 JSR 107,您可以明确排除配置中的某些异常,也许这会给您留出继续的余地。 (参见 nonCachedExceptions)Spring 的抽象提供了 JSR107 注释支持。
  • 仅供参考 - 还有一个 condition 参数,也可以使用。在我的情况下,我需要 condition="#pk != null" 以便当参数为 null 时缓存不会抛出 hv000028: unexpected exception during isvalid call :)
  • 我复制粘贴您的代码并得到了出现意外错误(类型=内部服务器错误,状态=500)。为缓存操作返回空键(也许您在没有调试信息的类上使用命名参数?) Builder[public java.util.Map
  • 以上@tftd 建议使用条件="#pk != null"。如果您需要使用“同步”,这很有用,因为“除非”在使用“同步”时不起作用。
【解决方案2】:

更新此答案现已过时,对于 Spring 3.2 及更高版本,请参阅 Tech Trip 的 answer,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();
}

【讨论】:

    【解决方案3】:

    如果是Spring注解

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

    不行,你可以试试:

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

    它对我有用。

    【讨论】:

    • 你是说unless="#result == null" 吗?
    • 缓存,除非结果!= null_means _Cache if result == null。因此,正确的表达式是unless="#result==null",这意味着 Cache unless result == null 这意味着 Cache if result != null
    猜你喜欢
    • 1970-01-01
    • 2018-02-07
    • 2021-08-22
    • 2019-08-28
    • 2020-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多