概述
緩存包是mybatis對緩存的抽象和實現,可爲其他模塊提供緩存的支持。其設計也很值得借鑑。
大概分爲
- 緩存定義(抽象、異常、緩存鍵)
- 緩存實現
- 緩存裝飾者
可以重點在於緩存策略裝飾模式實現,可以複習複習裝飾模式啦。
Cache
public interface Cache {
String getId();
void putObject(Object key, Object value);
Object getObject(Object key);
Object removeObject(Object key);
void clear();
int getSize();
}
緩存容器基礎抽象,定義了基礎的緩存操作。
所有緩存容器實現類。其中只有PerpetualCache是真正的緩存實現類,其餘都是裝飾實現類。
PerpetualCache
永不過期緩存容器,實現比較簡單,最基礎的單機緩存容器實現。
public class PerpetualCache implements Cache {
// 緩存容器id
private final String id;
// 緩存容器載體
private Map<Object, Object> cache = new HashMap<>();
}
緩存操作基於HashMap,非常簡單。
Decorator
decorator有一個單獨的子包,全部放着緩存的裝飾類。
- BlockingCache
- FifoCache
- loggingCache
- LruCache
- ScheduledCache
- SerializedCache
- SoftCache
- SynchronizedCache
- TransactionalCache
- WeakCache
通過名字可以很容易就辨認出該實現的作用。
讓我們先複習下裝飾模式
這是裝飾模式的類圖
Component:抽象組件
ConcreteComponent:組件實現類
Decorator:裝飾實現類,實現抽象組件,組合ConcreteComponent,並在其上額外添加功能。
好處是添加功能時無需對之前類進行修改,職責也更加清晰。
讓我們來看看mybatis緩存中Decorator的實現。
FifoCache
public class FifoCache implements Cache {
private final Cache delegate;
/**
* 雙端隊列
*/
private final Deque<Object> keyList;
/**
* 固定大小
*/
private int size;
public FifoCache(Cache delegate) {
this.delegate = delegate;
this.keyList = new LinkedList<>();
this.size = 1024;
}
}
設置初始緩存大小,使用雙端隊列進行控制key大小。實現也比較簡單。
WeakCache
虛引用緩存,緩存的值使用虛引用包裝。
又被迫複習下虛引用的知識,gc時如果發現內存不足會清楚虛引用包裹的對象。
public class WeakCache implements Cache {
// 雙端隊列保存一定數量的值,避免被gc
private final Deque<Object> hardLinksToAvoidGarbageCollection;
// 引用隊列,被gc清楚的值會放在裏面
private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
private final Cache delegate;
// 隊列大小
private int numberOfHardLinks;
public WeakCache(Cache delegate) {
this.delegate = delegate;
this.numberOfHardLinks = 256;
this.hardLinksToAvoidGarbageCollection = new LinkedList<>();
this.queueOfGarbageCollectedEntries = new ReferenceQueue<>();
}
@Override
public String getId() {
return delegate.getId();
}
@Override
public int getSize() {
removeGarbageCollectedItems();
return delegate.getSize();
}
public void setSize(int size) {
this.numberOfHardLinks = size;
}
@Override
public void putObject(Object key, Object value) {
removeGarbageCollectedItems();
delegate.putObject(key, new WeakEntry(key, value, queueOfGarbageCollectedEntries));
}
@Override
public Object getObject(Object key) {
Object result = null;
@SuppressWarnings("unchecked") // assumed delegate cache is totally managed by this cache
WeakReference<Object> weakReference = (WeakReference<Object>) delegate.getObject(key);
if (weakReference != null) {
result = weakReference.get();
if (result == null) {
delegate.removeObject(key);
} else {
hardLinksToAvoidGarbageCollection.addFirst(result);
if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {
hardLinksToAvoidGarbageCollection.removeLast();
}
}
}
return result;
}
@Override
public Object removeObject(Object key) {
removeGarbageCollectedItems();
return delegate.removeObject(key);
}
@Override
public void clear() {
hardLinksToAvoidGarbageCollection.clear();
removeGarbageCollectedItems();
delegate.clear();
}
private void removeGarbageCollectedItems() {
WeakEntry sv;
while ((sv = (WeakEntry) queueOfGarbageCollectedEntries.poll()) != null) {
delegate.removeObject(sv.key);
}
}
private static class WeakEntry extends WeakReference<Object> {
private final Object key;
private WeakEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {
super(value, garbageCollectionQueue);
this.key = key;
}
}
}
每次操作時調用removeGarbageCollectedItems()方法,用於清楚已經被gc清除了的緩存值。
其他
還有一些策略緩存裝飾幾乎使用的很少,參考參考即可。
總結
緩存包 比較清晰簡單,但是有借鑑的地方的。在我們業務開發時,可以也爲應用設計一個獨立的緩存包,後續甚至可以獨立爲服務,一些緩存策略也可以使用裝飾模式來實現。代碼肯定會比直接操作redis清晰許多。
That’s All