88

弹簧文档

@Cacheable(value="bookCache", key="isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

如何指定@Cachable使用isbncheckWarehouse作为键?

4

6 回答 6

104

更新:如果没有另外指定,当前的 Spring 缓存实现使用所有方法参数作为缓存键。如果您想使用选定的键,请参阅Arjan 的答案,它使用 SpEL 列表{#isbn, #includeUsed} ,这是创建唯一键的最简单方法。

来自Spring 文档

随着 Spring 4.0 的发布,默认的密钥生成策略发生了变化。Spring早期版本使用了key生成策略,对于多个key参数,只考虑参数的hashCode(),不考虑equals();这可能会导致意外的键冲突(有关背景信息,请参阅 SPR-10237)。新的“SimpleKeyGenerator”在这种情况下使用复合键。

春季 4.0 之前

我建议您将 Spel 表达式中的参数值与类似的东西连接起来key="#checkWarehouse.toString() + #isbn.toString()"),我相信这应该可以作为 org.springframework.cache.interceptor.ExpressionEvaluator 返回的对象,稍后将其用作键,因此您不必提供在int你的 SPEL 表达式中。

至于具有高碰撞概率的哈希码 - 您不能将其用作密钥。

该线程中有人建议使用T(java.util.Objects).hash(#p0,#p1, #p2),但它不会起作用,而且这种方法很容易破坏,例如我使用了来自SPR-9377的数据:

    System.out.println( Objects.hash("someisbn", new Integer(109), new Integer(434)));
    System.out.println( Objects.hash("someisbn", new Integer(110), new Integer(403)));

这两行都在我的环境中打印 -636517714。

PS 实际上在我们的参考文档中

@Cacheable(value="books", key="T(someType).hash(#isbn)") 
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

我认为这个例子是错误的和误导性的,应该从文档中删除,因为键应该是唯一的。

PPS 还可以查看https://jira.springsource.org/browse/SPR-9036,了解有关默认密钥生成的一些有趣想法。

为了正确起见,我想补充一下,作为一个有趣的数学/计算机科学事实,与内置散列不同,使用像 MD5 或 SHA256 这样的安全加密 散列函数,由于此类函数的属性,这绝对是可能的任务,但每次都计算它可能太昂贵了,请查看 Dan Boneh 密码学课程以了解更多信息。

于 2012-12-29T01:16:51.293 回答
90

在使用 Spring 3.2 进行一些有限测试后,似乎可以使用 SpEL 列表:{..., ..., ...}. 这也可以包括null值。Spring 将列表作为键传递给实际的缓存实现。使用 Ehcache 时,有时会调用List#hashCode(),它会考虑其所有项目。(我不确定 Ehcache 是否依赖哈希码。)

我将它用于共享缓存,其中我也在键中包含方法名称,而 Spring 默认键生成器不包含. 这样,我可以轻松地擦除(单个)缓存,而不会(太多......)冒着为不同方法匹配键的风险。像:

@Cacheable(value="bookCache", 
  key="{ #root.methodName, #isbn?.id, #checkWarehouse }")
public Book findBook(ISBN isbn, boolean checkWarehouse) 
...

@Cacheable(value="bookCache", 
  key="{ #root.methodName, #asin, #checkWarehouse }")
public Book findBookByAmazonId(String asin, boolean checkWarehouse)
...

当然,如果许多方法都需要这个并且您总是使用所有参数作为密钥,那么还可以定义一个自定义密钥生成器,其中包含类和方法名称:

<cache:annotation-driven mode="..." key-generator="cacheKeyGenerator" />
<bean id="cacheKeyGenerator" class="net.example.cache.CacheKeyGenerator" />

...和:

public class CacheKeyGenerator 
  implements org.springframework.cache.interceptor.KeyGenerator {

    @Override
    public Object generate(final Object target, final Method method, 
      final Object... params) {

        final List<Object> key = new ArrayList<>();
        key.add(method.getDeclaringClass().getName());
        key.add(method.getName());

        for (final Object o : params) {
            key.add(o);
        }
        return key;
    }
}
于 2013-07-01T14:13:37.787 回答
5

您可以使用 Spring-EL 表达式,例如在 JDK 1.7 上:

@Cacheable(value="bookCache", key="T(java.util.Objects).hash(#p0,#p1, #p2)")
于 2012-12-28T16:24:17.070 回答
3

您可以使用 Spring SimpleKey 类

@Cacheable(value = "bookCache", key = "new org.springframework.cache.interceptor.SimpleKey(#isbn, #checkWarehouse)")
于 2019-11-20T10:09:46.100 回答
1

这将起作用

@Cacheable(value="bookCache", key="#checkwarehouse.toString().append(#isbn.toString())")
于 2013-05-31T12:38:09.410 回答
-1

用这个

@Cacheable(value="bookCache", key="#isbn + '_' + #checkWarehouse + '_' + #includeUsed")
于 2014-06-11T03:04:56.540 回答