對於宇宙中每個問題的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){
...
}
同樣,這還遠遠沒有被很好的測試,但它給你一種方法做你想做的。如果您使用其他緩存管理器,則可以類似地擴展其緩存實施。