2015-09-08 2 views
4

Je met en cache les résultats d'une fonction à l'aide de l'annotation @cacheable. J'ai 3 caches différents et la clé pour chacun est l'identifiant de l'utilisateur actuellement connecté concaténé avec un argument dans la méthode. Lors d'un événement donné, je souhaite supprimer toutes les entrées de cache dont la clé commence par cet ID utilisateur particulier. Par exemple:Comment utiliser la clé, dans une condition de l'annotation Cacheable

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

Je veux l'annotation evict de cache quelque chose comme:

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

Mais lorsque je tente de mettre en œuvre cela, il me donne une erreur:

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

Qu'est-ce que est la bonne façon de mettre en œuvre cela?

+0

Question intéressante. – Scooby

Répondre

5

Toutes les annotations Spring Cache (c'est-à-dire @Cacheable, @CacheEvict, etc.) fonctionnent sur 1 entrée de cache par opération. @CacheEvict prend en charge la suppression du cache entier (avec l'attribut allEntries, mais ignore la clé dans ce cas), mais il n'est pas sélectif (capable) pour effacer un ensemble partiel d'entrées basé sur un motif de clé en une seule opération comme vous l'avez décrit .

La principale raison derrière cela est l'abstraction de l'interface Spring Cache elle-même, où la méthode evict(key:Object) prend un seul argument clé. Mais techniquement, cela dépend de l'implémentation de cache sous-jacente (par exemple GemfireCache), qui devrait supporter l'expulsion de toutes les entrées dont les clés correspondent à un motif particulier, ce qui n'est généralement pas le cas de la plupart des caches. pas Google Goyave Cache soit, voir here et here)

Cela ne veut pas dire que vous ne pouvez absolument pas atteindre votre objectif.. Ce n'est tout simplement pas quelque chose d'out-of-the-box. La chose intéressante, sans quelques problèmes techniques avec votre approche, est que votre condition réalise en quelque sorte ce que vous voulez ... une expulsion du cache ne se produit que si la clé satisfait à la condition. Cependant, votre méthode annotée @CacheEvict manque simplement la "clé", d'où l'erreur. Donc, quelque chose comme ce qui suit satisferait SPEL dans votre état ...

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

Cependant, vous devez spécifier la clé comme argument dans ce cas. Mais, vous ne voulez pas de clé spécifique, vous voulez un motif correspondant à plusieurs touches. Ainsi, renoncer à la condition et il suffit d'utiliser ...

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

A titre d'exemple, en utilisant Goyave en tant que fournisseur de cache, vous devez maintenant fournir une implémentation « personnalisée » extension 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); 
    } 
    } 
} 

maintenant étendre juste le GuavaCacheManager à votre plug-in "sur mesure" GuavaCache (CustomGuavaCache) ...

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

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

Cette approche profite de la méthode Cache'sinvalidateAll(keys:Iterable) de goyave. Et, bien sûr, vous pouvez utiliser le support Regex de Java pour effectuer la "correspondance" sur les clés souhaitées à expulser à l'intérieur de la méthode isMatch(key, pattern).

Donc, je ne l'ai pas testé, mais cela (ou quelque chose de similaire) devrait permettre d'atteindre (presque) ce que vous voulez (les doigts croisés ;-)

Hope this helps!

Cheers, John