緩存條目Listener
事件與Listener
JCache中定義了緩存事件的抽象類CacheEntryEvent<K,V>,以及事件類型EventType枚舉類,包括了四種事件類型,定義見下:
public enum EventType {
CREATED, //創建
UPDATED, //更新
REMOVED, //刪除
EXPIRED //過期
}
這些事件將被傳播到註冊在Cache中的CacheEntryListener,這個接口是一個標記接口,與四種事件類型對應的Listener接口都繼承了這個接口,分別是CacheEntryCreatedListener、CacheEntryUpdatedListener、CacheEntryRemovedListener和CacheEntryExpiredListener。
Listener的註冊
Listener並不一定是在Cache的進程內的,爲了避免可能不支持序列化實例的註冊,需要使用CacheEntryListenerConfigurations(具體類MutableCacheEntryListenerConfiguration)。
開發可在配置時通過MutableConfiguation.addCacheEntryListenerConfiguration方法增加緩存的Listener配置,或在運行期通過Cache.registerCacheEntryListener方法來註冊Listener。Listener可以通過Cache.deregisterCacheEntryListener方法來註銷註冊。
多個CacheEntryListenerConfiguration可以增加到Configuration中,當緩存初始化時,它使用註冊的Factory來創建CacheEntryListener。針對一個緩存可以存在多個Listener對應相同或者不同的EventType。在Listener創建或在其間派發事件,框架並不能保證其先後順序。
Listener的調用
緩存Listener
●在緩存條目變化後被觸發。
●在同步方式,對於一個給定的鍵,以該事件發生的順序觸發Listener,並阻塞調用線程,直到Listener返回。
●在異步方式,以未定義的順序遍歷多個事件,但針對相同的Key,事件必須按照發生的順序進行處理。
Listener是觀察者模式,同時Listener拋出一個異常並不會導致緩存操作失敗。
在一個Listener中修改緩存的值可能會導致死鎖,檢測和響應死鎖是特定於實現的。
緩存實現中已註冊的Listener針對每個事件將最多被調用一次。
一個Listener並不一定在事件發生的進程內執行,在分佈式環境中Listener可以在任何地方實現。
Listener可以有一個CacheEntryEventFilter,通過CacheEntryListenerConfiguration來配置。
Filter和同樣Listener,它並不一定在事件發生的進程內執行。在分佈式環境中,Filter可能實現在任何能提供最好性能的節點處。
下表總結了由每個緩存操作調用的Listener操作。條件是在操作前的條目的狀態,過期始終是“否”,過期的確切時間基於緩存的特定實現。
方法 |
創建 |
過期 |
刪除 |
更新 |
boolean containsKey(K key) |
否 |
否 |
否 |
否 |
V get(K key) |
是, 如果是通過read-through創建 |
否 |
否 |
否 |
Map<K,V> getAll(Collection<? extends K> keys) |
是, 如果是通過read-through創建 |
否 |
否 |
否 |
V getAndPut(K key, V value) |
是,如果不存在 |
否 |
否 |
是,如果存在 |
V getAndRemove(K key) |
否 |
否 |
是,如果存在 |
否 |
V getAndReplace(K key, V value) |
否 |
否 |
否 |
是,如果存在 |
<T> T invoke(K key, EntryProcessor<K, V> entryProcessor); |
是, 如果調用setValue()創建或者 調用getValue()通過read-through創建 |
否 |
是, 如果調用remove() |
是, 如果調用 setValue() 更新 |
<T> Map<K, T> invokeAll(Set<? extends K> keys, EntryProcessor<K, V, T> entryProcessor, Object... arguments); |
是, 如果調用setValue()創建或者 調用getValue()通過read-through創建 |
否 |
是, 如果調用remove() |
是, 如果調用 setValue() 更新 |
Iterator<Cache.Entry<K, V>> iterator() |
否 |
否 |
是, 如果調用remove() |
否 |
void loadAll(Set<? extends K> keys,boolean replaceExistingValues, CompletionListener completionListener);
|
是,如果不存在 |
否 |
否 |
是,如果存在 |
void put(K key, V value) |
是,如果不存在 |
否 |
否 |
是,如果存在 |
void putAll(Map<? extends K,? extends V> map) |
是,如果不存在 |
否 |
否 |
是,如果存在 |
boolean putIfAbsent(K key, V value) |
是,如果不存在 |
否 |
否 |
否 |
boolean remove(K key) |
否 |
否 |
是,如果存在 |
否 |
boolean remove(K key, V oldValue) |
否 |
否 |
是,如果存在並且相等 |
否 |
void removeAll() |
否 |
否 |
是,如果存在 |
否 |
void removeAll(Set<? extends K> keys) |
否 |
否 |
是,如果存在 |
否 |
boolean replace(K key, V value) |
否 |
否 |
否 |
是,如果存在 |
boolean replace(K key, V oldValue, V newValue) |
否 |
否 |
否 |
是,如果存在並且相等 |
緩存條目執行器
一個javax.cache.Cache.EntryProcessor是一個可調用的功能,就像一個java.util.concurrent.Callable,應用程序可以用它來有效地執行組合的原子性的緩存操作,包括訪問,更新和緩存條目,而不需要顯式的鎖或事務。
例如,可能希望檢查一個緩存條目的值,計算出新的值,更新條目,並返回一些其他的值的原子操作,一個應用程序可以使用自定義的EntryProcessor實現來完成此功能。
javax.cache.Cache.EntryProcessor定義見下:
public interface EntryProcessor<K, V,T> {
/**
* 處理一個條目。
* @param entry 緩存條目
* @param arguments 處理的參數集合
* @return 處理的結果
*/
T process(Cache.MutableEntry<K, V> entry, Object... arguments);
}
爲了在緩存條目上調用EntryProcessor,應用必須使用Cache.invoke方法調用單個key的處理器及使用Cache.invokeAll調用一個key集合的處理器。
下面的例子演示了使用EntryProcessor自動增加緩存條目的值。
CachingProvider provider = Caching.getCachingProvider();
CacheManager manager = provider.getCacheManager();
MutableConfiguration<String, Integer> configuration =
new MutableConfiguration<String, Integer>()
.setTypes(String.class, Integer.class);
manager.createCache("example", configuration);
Cache<String, Integer> cache = manager.getCache(
"example", configuration);
String key = "counter";
cache.put(key, 1);
int previous = cache.invoke(key,
newIncrementProcessor<String>());
assert previous == 1;
assert cache.get(key) == 2;
IncrementProcessor定義如下。
public static class IncrementProcessor<K>
implements Cache.EntryProcessor<K, Integer, Integer> {
@Override
public Integer process(Cache.MutableEntry<K, Integer> entry,
Object... arguments){
if (entry.exists()) {
Integer current = entry.getValue();
entry.setValue(current + 1);
return current;
} else {
entry.setValue(0);
return -1;
}
}
}
支持遠程或分佈式緩存拓撲的實現可以選擇在遠程進程中執行條目處理器。在這種情況下實現可能需要獲取EntryProcessors,調用的參數和返回類型實現java.lang.Serializable或以某種方式序列化。另外一個可選方法是實現可以選擇簡單序列化EntryProcessor類名,連同指定的調用參數,通過在遠程實例化EntryProcessor類和反序列化調用的參數來執行遠程調用請求。
當一個EntryProcessor被調用時,一個應用程序將永遠不會看到其調用MutableEntry的getValue,etValue, remove方法產生的中間事件或副作用,而只能觀察一個EntryProcessor上對緩存條目進行操作的最終結果。