Podobnie jak w przypadku 99% wszystkich pytań we wszechświecie, odpowiedź brzmi: to zależy. Jeśli twój menedżer pamięci podręcznej zaimplementuje coś, co się z tym wiąże, to świetnie. Ale tak się nie wydaje.
Jeśli używasz SimpleCacheManager
, który jest podstawowym menedżerem pamięci podręcznej w pamięci dostarczonym przez Spring, prawdopodobnie używasz ConcurrentMapCache
, który również pochodzi ze Spring. Chociaż nie jest możliwe rozszerzenie ConcurrentMapCache
w celu obsługi symboli wieloznacznych w kluczach (ponieważ magazyn pamięci podręcznej jest prywatny i nie masz do niego dostępu), możesz po prostu użyć go jako inspiracji dla własnej implementacji.
Poniżej przedstawiono możliwą implementację (nie testowałem jej zbyt wiele, poza sprawdzaniem, czy działa). Jest to zwykła kopia ConcurrentMapCache
z modyfikacją metody evict()
. Różnica polega na tym, że ta wersja evict()
traktuje klucz, aby sprawdzić, czy jest to wyrażenie regularne. W takim przypadku następuje iteracja wszystkich kluczy w sklepie i usunięcie tych, które pasują do wyrażenia regularnego.
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 {
}
}
Ufam, że czytelnicy wiedzą, jak zainicjować menedżera pamięci podręcznej za pomocą niestandardowej implementacji pamięci podręcznej. Istnieje wiele dokumentacji, która pokazuje, jak to zrobić.Po projekt jest prawidłowo skonfigurowany, można użyć adnotacji zwykle tak:
@CacheEvict(value = { "cacheName" }, key = "'regex:#tenant'+'.*'")
public myMethod(String tenant){
...
}
Ponownie, jest to dalekie od odpowiednio przetestowane, ale to daje drogę do tego, co chcesz. Jeśli używasz innego menedżera pamięci podręcznej, możesz podobnie rozszerzyć implementację pamięci podręcznej.