@Cacheable(value="bookCache", key="isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
如何指定@Cachable
使用isbn
和checkWarehouse
作为密钥?
Answers:
更新:如果没有另外指定,当前的Spring缓存实现将所有方法参数用作缓存键。如果要使用选定的键,请参考Arjan的答案,该答案使用SpEL列表{#isbn, #includeUsed}
,这是创建唯一键的最简单方法。
随着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)));
在我的环境中,这两行都显示-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,由于这样的功能的特性IS这绝对有可能的任务,但是每次都进行计算可能会太昂贵,例如,请查看Dan Boneh密码学课程以了解更多信息。
T(someType).hash(#isbn)
在春季工作3.1.1
?
{ #root.methodName, #param1, #param2 }
,反正3.1.1是旧版本了。
与Spring 3.2的一些有限的测试后,似乎可以使用一个规划环境地政司名单:{..., ..., ...}
。这也可以包括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;
}
}
您可以使用Spring-EL表达式,例如在JDK 1.7上:
@Cacheable(value="bookCache", key="T(java.util.Objects).hash(#p0,#p1, #p2)")
@Cacheable(value="books", key="T(someType).hash(#isbn)") public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
-但它似乎是一个错误的和误导性的例子
您可以使用Spring SimpleKey类
@Cacheable(value = "bookCache", key = "new org.springframework.cache.interceptor.SimpleKey(#isbn, #checkWarehouse)")
这会工作
@Cacheable(value="bookCache", key="#checkwarehouse.toString().append(#isbn.toString())")
用这个
@Cacheable(value="bookCache", key="#isbn + '_' + #checkWarehouse + '_' + #includeUsed")