多个方法参数的@Cacheable键

时间:2012-12-28 16:14:09

标签: java spring ehcache

来自spring documentation

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

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

6 个答案:

答案 0 :(得分:68)

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

来自Arjan's answer

  

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

在Spring 4.0之前

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

对于具有高冲突概率的哈希码 - 不能将其用作密钥。

此线程中有人建议使用T(java.util.Objects).hash(#p0,#p1, #p2)但它不起作用,这种方法很容易破解,例如我使用了来自Spring Documentation的数据:

    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。

P.S。实际上在参考文档中我们有

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

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

P.P.S。另请参阅SPR-9377,了解有关默认密钥生成的一些有趣想法。

我想为了正确性而添加一个有趣的事实,即使用像SHA256这样的安全https://jira.springsource.org/browse/SPR-9036,由于这个函数的属性 IS 可能用于此任务,但每次计算它可能太贵了。

答案 1 :(得分:62)

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

我将它用于共享缓存,其中密钥中的include the method name也是Spring默认密钥生成器does not include。这样我就可以轻松擦除(单个)缓存,而不会(太多......)冒着不同方法的匹配键的风险。像:

@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;
    }
}

答案 2 :(得分:4)

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

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

答案 3 :(得分:0)

这将有效

@Cacheable(value="bookCache", key="#checkwarehouse.toString().append(#isbn.toString())")

答案 4 :(得分:0)

您可以使用Spring SimpleKey类

@Cacheable(value = "bookCache", key = "new org.springframework.cache.interceptor.SimpleKey(#isbn, #checkWarehouse)")

答案 5 :(得分:-2)

使用此

@Cacheable(value="bookCache", key="#isbn + '_' + #checkWarehouse + '_' + #includeUsed")