java 如何在 Cacheable 注释中的条件下使用密​​钥

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32460606/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 20:15:04  来源:igfitidea点击:

How do I use the key, in a condition in Cacheable annotation

javaspringehcachespring-cache

提问by Nandita Rao

I'm caching the results of a function using the @cacheable annotation. I have 3 different caches and the key for each one is the user id of the currently logged in user concatenated with an argument in the method . On a certain event I want to evict all the cache entries which have the key that starts with that particular user id. For example :

我正在使用 @cacheable 注释缓存函数的结果。我有 3 个不同的缓存,每个缓存的键是当前登录用户的用户 ID 与方法中的参数连接。在某个事件中,我想驱逐所有具有以该特定用户 ID 开头的键的缓存条目。例如 :

@Cacheable(value = "testCache1", key = "'abcdef'")

I want the cache evict annotation to be something like :

我希望缓存驱逐注释是这样的:

@CacheEvict(value = "getSimilarVendors", condition = "key.startsWith('abc')")

But when I try to implement this it gives me an error :

但是当我尝试实现它时,它给了我一个错误:

Property or field 'key' cannot be found on object of type'org.springframework.cache.interceptor.CacheExpressionRootObject' - maybe not      public?

What is the correct way to implement this?

实现这一点的正确方法是什么?

回答by John Blum

All of the Spring Cache annotations (i.e. @Cacheable, @CacheEvict, etc) work on 1 cache entry per operation. @CacheEvictdoes support clearing the entire cache (with the allEntriesattribute, however ignores the key in this case), but it is not selective (capable) in clearing a partial set of entries based on a key pattern in a single operation as you have described.

所有 Spring Cache 注释(即@Cacheable@CacheEvict等)针对每个操作处理 1 个缓存条目。 @CacheEvict确实支持清除整个缓存(使用allEntries属性,但在这种情况下会忽略键),但在您描述的单个操作中基于键模式清除部分条目集时没有选择性(能够)。

The main reason behind this is the Spring Cacheinterface abstraction itself, where the evict(key:Object)method takes a single key argument. But technically, it actually depends on the underlying Cache implementation (e.g. GemfireCache), which would need to support eviction on all entries who's keys match a particular pattern, which is typically, not the case for most caches (e.g. certainly not for GemFire, and not for Google Guava Cache either; see hereand here.)

这背后的主要原因是 Spring Cache接口抽象本身,其中evict(key:Object)方法采用单个键参数。但从技术上讲,它实际上取决于底层 Cache 实现(例如GemfireCache),它需要支持对所有键匹配特定模式的条目进行驱逐,这通常不是大多数缓存的情况(例如,GemFire 肯定不是,并且也不适用于 Google Guava Cache;请参阅此处此处。)

That is not to say you absolutely cannot achieve your goal. It's just not something supported out-of-the-box.

这并不是说您绝对无法实现目标。它只是不支持开箱即用的东西。

The interesting thing, minus some technical issues with your approach, is that your condition achieves sort of what you want... a cache eviction only occurs if the key satisfies the condition. However, you @CacheEvict annotated method is just missing the "key", hence the error. So, something like the following would satisfy the SpEL in your condition...

有趣的是,减去你的方法的一些技术问题,你的条件达到了你想要的......只有当键满足条件时才会发生缓存驱逐。但是,您 @CacheEvict 注释的方法只是缺少“键”,因此出现错误。因此,类似以下内容将满足您条件下的 SpEL ...

@CacheEvict(condition = "#key.startsWith('abc')")
public void someMethod(String key) {
  ...
}

However, you have to specify the key as an argument in this case. But, you don't want a specific key, you want a pattern matching several keys. So, forgo the condition and just use...

但是,在这种情况下,您必须将键指定为参数。但是,您不需要特定的键,而是需要匹配多个键的模式。所以,放弃条件,只使用......

@CacheEvict
public void someMethod(String keyPattern) {
  ...
}

By way of example, using Guava as the caching provider, you would now need to provide a "custom" implementation extending GuavaCache.

例如,使用 Guava 作为缓存提供者,您现在需要提供一个扩展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);
    }
  }
}

Now just extend the GuavaCacheManagerto plugin your "custom" GuavaCache(CustomGuavaCache)...

现在只需扩展GuavaCacheManager以插入您的“自定义” GuavaCacheCustomGuavaCache)...

public class CustomGuavaCacheManager extends org.springframework.cache.guava.GuavaCacheManager {

  @Override
  protected Cache createGuavaCache(String name) {
    return new CustomGuavaCache(name, createNativeGuavaCache(name), isAllowNullValues());
  }
}

This approach takes advantage of Guava's Cache'sinvalidateAll(keys:Iterable)method. And, of course, you could use Java's Regex support to perform the "matching" on the desired keys to be evicted inside the isMatch(key, pattern)method.

这种方法利用了 Guava 的Cache 的invalidateAll(keys:Iterable)方法。而且,当然,您可以使用 Java 的 Regex 支持对要在isMatch(key, pattern)方法内驱逐的所需键执行“匹配” 。

So, I have not tested this, but this (or something similar) should achieve (almost) what you want (fingers crossed ;-)

所以,我没有测试过这个,但是这个(或类似的东西)应该(几乎)达到你想要的(手指交叉;-)

Hope this helps!

希望这可以帮助!

Cheers, John

干杯,约翰