2013-07-19 132 views
11

有沒有在@CacheEvict中使用通配符的方法?Spring @CacheEvict使用通配符

我有一個多租戶應用程序,有時需要從租戶的緩存中清除所有數據,但不是系統中所有租戶的所有數據。

考慮以下方法:

@Cacheable(value="users", key="T(Security).getTenant() + #user.key") 
public List<User> getUsers(User user) { 
    ... 
} 

所以,我想這樣做:

@CacheEvict(value="users", key="T(Security).getTenant() + *") 
public void deleteOrganization(Organization organization) { 
    ... 
} 

反正有這樣做呢?

回答

1

對於宇宙中每個問題的99%,答案是:這取決於。如果你的緩存管理器實現了一些處理這個問題的東西,那很好但似乎並非如此。

如果您使用的是SimpleCacheManager,它是Spring提供的基本內存緩存管理器,您可能使用的是ConcurrentMapCache,它也附帶了Spring。雖然無法擴展ConcurrentMapCache來處理密鑰中的通配符(因爲緩存存儲是私有的,並且無法訪問),但您可以將其用作自己實現的靈感。

下面有一個可能的實現(除了檢查它是否工作以外,我沒有真正測試過它)。這是一個ConcurrentMapCache的簡單副本,並對evict()方法進行了修改。不同的是,evict()的這個版本將關鍵看作是否是正則表達式。在這種情況下,它遍歷存儲中的所有鍵並驅逐與正則表達式匹配的鍵。

package com.sigraweb.cache; 

import java.io.Serializable; 
import java.util.concurrent.ConcurrentHashMap; 
import java.util.concurrent.ConcurrentMap; 

import org.springframework.cache.Cache; 
import org.springframework.cache.support.SimpleValueWrapper; 
import org.springframework.util.Assert; 

public class RegexKeyCache implements Cache { 
    private static final Object NULL_HOLDER = new NullHolder(); 

    private final String name; 

    private final ConcurrentMap<Object, Object> store; 

    private final boolean allowNullValues; 

    public RegexKeyCache(String name) { 
     this(name, new ConcurrentHashMap<Object, Object>(256), true); 
    } 

    public RegexKeyCache(String name, boolean allowNullValues) { 
     this(name, new ConcurrentHashMap<Object, Object>(256), allowNullValues); 
    } 

    public RegexKeyCache(String name, ConcurrentMap<Object, Object> store, boolean allowNullValues) { 
     Assert.notNull(name, "Name must not be null"); 
     Assert.notNull(store, "Store must not be null"); 
     this.name = name; 
     this.store = store; 
     this.allowNullValues = allowNullValues; 
    } 

    @Override 
    public final String getName() { 
     return this.name; 
    } 

    @Override 
    public final ConcurrentMap<Object, Object> getNativeCache() { 
     return this.store; 
    } 

    public final boolean isAllowNullValues() { 
     return this.allowNullValues; 
    } 

    @Override 
    public ValueWrapper get(Object key) { 
     Object value = this.store.get(key); 
     return toWrapper(value); 
    } 

    @Override 
    @SuppressWarnings("unchecked") 
    public <T> T get(Object key, Class<T> type) { 
     Object value = fromStoreValue(this.store.get(key)); 
     if (value != null && type != null && !type.isInstance(value)) { 
      throw new IllegalStateException("Cached value is not of required type [" + type.getName() + "]: " + value); 
     } 
     return (T) value; 
    } 

    @Override 
    public void put(Object key, Object value) { 
     this.store.put(key, toStoreValue(value)); 
    } 

    @Override 
    public ValueWrapper putIfAbsent(Object key, Object value) { 
     Object existing = this.store.putIfAbsent(key, value); 
     return toWrapper(existing); 
    } 

    @Override 
    public void evict(Object key) { 
     this.store.remove(key); 
     if (key.toString().startsWith("regex:")) { 
      String r = key.toString().replace("regex:", ""); 
      for (Object k : this.store.keySet()) { 
       if (k.toString().matches(r)) { 
        this.store.remove(k); 
       } 
      } 
     } 
    } 

    @Override 
    public void clear() { 
     this.store.clear(); 
    } 

    protected Object fromStoreValue(Object storeValue) { 
     if (this.allowNullValues && storeValue == NULL_HOLDER) { 
      return null; 
     } 
     return storeValue; 
    } 

    protected Object toStoreValue(Object userValue) { 
     if (this.allowNullValues && userValue == null) { 
      return NULL_HOLDER; 
     } 
     return userValue; 
    } 

    private ValueWrapper toWrapper(Object value) { 
     return (value != null ? new SimpleValueWrapper(fromStoreValue(value)) : null); 
    } 

    @SuppressWarnings("serial") 
    private static class NullHolder implements Serializable { 
    } 
} 

我相信讀者知道如何使用自定義緩存實現來初始化緩存管理器。那裏有很多文檔說明如何做到這一點。你的項目被正確配置後,就可以正常使用註釋,像這樣:

@CacheEvict(value = { "cacheName" }, key = "'regex:#tenant'+'.*'") 
public myMethod(String tenant){ 
... 
} 

同樣,這還遠遠沒有被很好的測試,但它給你一種方法做你想做的。如果您使用其他緩存管理器,則可以類似地擴展其緩存實施。

3

的答案是:否。

而且它沒有簡單的方法來實現你想要的。

  1. Spring Cache註釋必須很簡單,以便高速緩存提供者很容易實現。
  2. 高效緩存必須簡單。有一個關鍵和價值。如果在緩存中找到密鑰,則使用該值,否則計算值並將其放入緩存。高效密鑰必須有快速和誠實等於()哈希碼()。假設你從一個租戶緩存了許多對(鍵,值)。爲了提高效率,不同的密鑰應該有不同的hashcode()。你決定驅逐整個房客。在緩存中找到租戶元素並不容易。您必須遍歷屬於租戶的所有緩存對並丟棄對。這不是有效的。它不是原子的,所以它很複雜,需要一些同步。同步效率不高。

因此沒有。

但是,如果你找到一個解決方案告訴我,因爲你想要的功能是非常有用的。