我正在使用@cacheable注释来缓存函数的结果。 我有3个不同的缓存,每个缓存的关键是当前登录用户的用户ID与方法中的参数连接。 在某个事件中,我想要驱逐所有具有以该特定用户ID开头的密钥的缓存条目。 例如:
@Cacheable(value = "testCache1", key = "'abcdef'")
我希望缓存逐出注释类似于:
@CacheEvict(value = "getSimilarVendors", condition = "key.startsWith('abc')")
但是当我尝试实现它时,它给了我一个错误:
Property or field 'key' cannot be found on object of type'org.springframework.cache.interceptor.CacheExpressionRootObject' - maybe not public?
实现此目的的正确方法是什么?
答案 0 :(得分:9)
所有Spring Cache注释(即@Cacheable
,@CacheEvict
等)都在每个操作的1个缓存条目上工作。 @CacheEvict
支持清除整个缓存(使用allEntries
属性,但在这种情况下忽略密钥),但它没有选择性(有能力)根据密钥模式清除部分条目集在你所描述的单一操作中。
这背后的主要原因是Spring Cache接口抽象本身,其中evict(key:Object)方法采用单个键参数。但从技术上讲,它实际上取决于底层的Cache实现(例如GemfireCache),它需要支持驱逐所有密钥与特定模式匹配的条目,这通常不是大多数缓存的情况(例如,当然不适用于GemFire,也不适用于Google Guava Cache;请参阅here和here。)
这并不是说你绝对无法实现目标。它不是开箱即用的支持。
有趣的是,减去你的方法的一些技术问题,就是你的条件达到了你想要的......只有当钥匙满足条件时才会出现缓存驱逐。但是,@ CacheEvict带注释的方法只是缺少"键",因此错误。所以,像你这样的东西会满足你的条件下的SpEL ......
@CacheEvict(condition = "#key.startsWith('abc')")
public void someMethod(String key) {
...
}
但是,在这种情况下,您必须将键指定为参数。但是,您不需要特定的密钥,您需要一个匹配多个密钥的模式。所以,放弃条件,只使用......
@CacheEvict
public void someMethod(String keyPattern) {
...
}
举例来说,使用Guava作为缓存提供程序,您现在需要提供一个" custom"实施延长GuavaCache。
public class CustomGuavaCache extends org.springframework.cache.guava.GuavaCache {
protected boolean isMatch(String key, String pattern) {
...
}
protected boolean isPattern(String key) {
...
}
@Override
public void evict(Object key) {
if (key instanceof String && isPattern(key.toString()))) {
Map<String, Object> entries = this.cache.asMap();
Set<String> matchingKeys = new HashSet<>(entries.size());
for (String actualKey : entries.keySet()) {
if (isMatch(actualKey, key.toString()) {
matchingKeys.add(actualKey);
}
}
this.cache.invalidateAll(matchingKeys);
}
else {
this.cache.invalidate(key);
}
}
}
现在只需将GuavaCacheManager扩展为插入您的&#34; custom&#34; GuavaCache
(CustomGuavaCache
)...
public class CustomGuavaCacheManager extends org.springframework.cache.guava.GuavaCacheManager {
@Override
protected Cache createGuavaCache(String name) {
return new CustomGuavaCache(name, createNativeGuavaCache(name), isAllowNullValues());
}
}
这种方法利用了Guava的Cache's invalidateAll(keys:Iterable)方法。当然,您可以使用Java的Regex支持来执行&#34;匹配&#34;在isMatch(key, pattern)
方法中要驱逐的所需键上。
所以,我没有测试过这个,但是这个(或类似的东西)应该达到(几乎)你想要的东西(手指交叉; - )
希望这有帮助!
干杯, 约翰