如何在Cacheable注释中使用该键 [英] How do I use the key, in a condition in Cacheable annotation

查看:1563
本文介绍了如何在Cacheable注释中使用该键的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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(value = "testCache1", key = "'abcdef'")

我希望缓存evict注释类似于:

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?

推荐答案

所有Spring Cache注释(即 @Cacheable @CacheEvict 等)每个操作在1个缓存条目上工作。 @CacheEvict 支持清除整个缓存(使用 allEntries 属性,但在这种情况下会忽略密钥),但是根据你所描述的单一操作中的关键模式,它没有选择性(有能力)清除部分条目。

All of the Spring Cache annotations (i.e. @Cacheable, @CacheEvict, etc) work on 1 cache entry per operation. @CacheEvict does support clearing the entire cache (with the allEntries attribute, 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 缓存界面抽象本身,其中 evict(key:Object)方法采用单个关键参数。但从技术上讲,它实际上取决于底层的Cache实现(例如 GemfireCache ),它需要支持驱逐所有键与特定模式匹配的条目,这通常不是这种情况对于大多数缓存(例如,当然不适用于GemFire,也不适用于Google Guava Cache;请参阅这里这里。)

The main reason behind this is the Spring Cache interface 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 here and here.)

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

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

有趣的是,减去你的方法的一些技术问题,就是你的病情达到你想要的水平...如果密钥满足条件,则仅发生缓存逐出。但是,@ CacheEvict带注释的方法只是缺少键,因此错误。因此,类似下面的内容将满足您的条件下的SpEL ...

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(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) {
  ...
}

举例来说,使用Guava作为缓存提供者,您现在需要提供一个自定义实现,扩展 GuavaCache

By way of example, using Guava as the caching provider, you would now need to provide a "custom" implementation extending 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 插入你的自定义 GuavaCache CustomGuavaCache )...

Now just extend the GuavaCacheManager to plugin your "custom" 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支持在 isMatch(key,pattern)方法中对所需的键执行匹配。

This approach takes advantage of Guava's Cache's invalidateAll(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.

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

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

希望这会有所帮助!

干杯,
John

Cheers, John

这篇关于如何在Cacheable注释中使用该键的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆