多个方法参数的@Cacheable键

phu*_*ury 66 java spring ehcache

春季文档:

@Cacheable(value="bookCache", key="isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
Run Code Online (Sandbox Code Playgroud)

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

Bor*_*hov 77

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

Spring文档

随着Spring 4.0的发布,默认密钥生成策略发生了变化.早期版本的Spring使用了一种密钥生成策略,对于多个关键参数,只考虑参数的hashCode()而不是equals(); 这可能会导致意外的键碰撞(参见SPR-10237的背景知识).新的'SimpleKeyGenerator'在这种情况下使用复合键.

在Spring 4.0之前

我建议你把Spel表达式中的参数值用类似的东西连接起来key="#checkWarehouse.toString() + #isbn.toString()"),我相信这应该像org.springframework.cache.interceptor.ExpressionEvaluator一样返回Object,后来用作键,所以你不必提供一个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)));
Run Code Online (Sandbox Code Playgroud)

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

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

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

我认为这个例子是错误的并且具有误导性,应该从文档中删除,因为密钥应该是唯一的.

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

我想补充的正确性着想,作为一个有趣的事实,使用安全的加密哈希函数像SHA256,由于这类函数的性质IS此任务可能的,但计算它每次会过于昂贵.

  • 匿名downvotes非常有用!对不起,目前还没有心灵感应. (3认同)

Arj*_*jan 64

在使用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)
...
Run Code Online (Sandbox Code Playgroud)

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

<cache:annotation-driven mode="..." key-generator="cacheKeyGenerator" />
<bean id="cacheKeyGenerator" class="net.example.cache.CacheKeyGenerator" />
Run Code Online (Sandbox Code Playgroud)

...有:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • _bit_ 晚了,@linqu,但默认密钥生成器不包含方法名称。如果要为多个方法使用单个缓存,则需要包含方法名称,如果两个方法可以具有相同的参数。 (2认同)

Bij*_*men 5

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

@Cacheable(value="bookCache", key="T(java.util.Objects).hash(#p0,#p1, #p2)")
Run Code Online (Sandbox Code Playgroud)