@(MyBatis)[Cache]
MyBatis源碼分析——Cache接口以及實現
Cache接口
MyBatis中的Cache以SPI實現,給需要集成其它Cache或者自定義Cache提供了接口。
public interface Cache {
String getId();
void putObject(Object key, Object value);
Object getObject(Object key);
Object removeObject(Object key);
void clear();
int getSize();
ReadWriteLock getReadWriteLock();
}

Cache實現
Cache的實現類中,Cache有不同的功能,每個功能獨立,互不影響,則對於不同的Cache功能,這里使用了裝飾者模式實現。
PerpetualCache
作為為最基礎的緩存類,底層實現比較簡單,直接使用了HashMap。
FifoCache
FIFO回收策略,裝飾類,內部維護了一個隊列,來保證FIFO,一旦超出指定的大小,則從隊列中獲取Key並從被包裝的Cache中移除該鍵值對。
public class FifoCache implements Cache {
// 被包裝的類
private final Cache delegate;
// 隊列,用來維持FIFO
private LinkedList<Object> keyList;
// 最大可容納的大小
private int size;
public FifoCache(Cache delegate) {
this.delegate = delegate;
this.keyList = new LinkedList<Object>();
this.size = 1024;
}
@Override
public void putObject(Object key, Object value) {
// 將Key放入隊列中,並且檢查一遍,如果滿了則移除隊列頭部的元素
cycleKeyList(key);
// 執行真正的操作
delegate.putObject(key, value);
}
private void cycleKeyList(Object key) {
// 將Key放入隊列
keyList.addLast(key);
if (keyList.size() > size) {
// 超出指定容量,移除隊列頭部Key
Object oldestKey = keyList.removeFirst();
// 從緩存中移除Key對應的值
delegate.removeObject(oldestKey);
}
}
// 省略部分代碼
...
}
LoggingCache
日志功能,裝飾類,用於記錄緩存的命中率,如果開啟了DEBUG模式,則會輸出命中率日志。
public class LoggingCache implements Cache {
private Log log;
private Cache delegate;
// 請求次數
protected int requests = 0;
// 命中次數
protected int hits = 0;
public LoggingCache(Cache delegate) {
this.delegate = delegate;
this.log = LogFactory.getLog(getId());
}
@Override
public Object getObject(Object key) {
requests++;
final Object value = delegate.getObject(key);
if (value != null) {
// 命中
hits++;
}
if (log.isDebugEnabled()) {
// 如果開啟了DEBUG模式,則輸出命中率
log.debug("Cache Hit Ratio [" + getId() + "]: " + getHitRatio());
}
return value;
}
// 獲取命中率
private double getHitRatio() {
return (double) hits / (double) requests;
}
// 省略部分代碼
...
}
LruCache
LRU回收策略,裝飾類,在內部保存一個LinkedHashMap,用以實現LRU。
public class LruCache implements Cache {
private final Cache delegate;
private Map<Object, Object> keyMap;
private Object eldestKey;
public LruCache(Cache delegate) {
this.delegate = delegate;
// 初始化設置LRU回收的邊界容量
setSize(1024);
}
public void setSize(final int size) {
keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
private static final long serialVersionUID = 4267176411845948333L;
// 鍵值移除策略,當大於指定容量時則移除最近最少使用的key/value
protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
boolean tooBig = size() > size;
if (tooBig) {
// 保存需要移除的鍵,因為在被包裝的類中並不知道什么鍵需要移除
eldestKey = eldest.getKey();
}
return tooBig;
}
};
}
@Override
public void putObject(Object key, Object value) {
delegate.putObject(key, value);
// 將當前Key放到LRU的Map中,如果大於指定容量,則移除篩選的鍵值對
cycleKeyList(key);
}
@Override
public Object getObject(Object key) {
// 讓當前LRU的Map知道使用過
keyMap.get(key); //touch
return delegate.getObject(key);
}
@Override
public Object removeObject(Object key) {
// 這里沒有移除當前維護的key,不過在后續也會被回收,可以忽略
return delegate.removeObject(key);
}
private void cycleKeyList(Object key) {
keyMap.put(key, key);
if (eldestKey != null) {
// 從Cache中移除掉LRU篩選出的鍵值對
delegate.removeObject(eldestKey);
eldestKey = null;
}
}
// 省略部分代碼...
}
ScheduledCache
定時清空Cache,但是並沒有開始一個定時任務,而是在使用Cache的時候,才去檢查時間是否到了。
public class ScheduledCache implements Cache {
private Cache delegate;
// 清除的時間間隔
protected long clearInterval;
// 上一次清除的時間
protected long lastClear;
public ScheduledCache(Cache delegate) {
this.delegate = delegate;
this.clearInterval = 60 * 60 * 1000; // 1 hour
this.lastClear = System.currentTimeMillis();
}
public void setClearInterval(long clearInterval) {
this.clearInterval = clearInterval;
}
@Override
public Object getObject(Object key) {
if (clearWhenStale()) {
return null;
} else {
return delegate.getObject(key);
}
}
private boolean clearWhenStale() {
if (System.currentTimeMillis() - lastClear > clearInterval) {
// 時間到了,清空
clear();
return true;
}
return false;
}
@Override
public void clear() {
// 更新清空時間
lastClear = System.currentTimeMillis();
delegate.clear();
}
// 省略部分代碼
}
SynchronizedCache
同步Cache,實現比較簡單,直接使用synchronized修飾方法。
public class SynchronizedCache implements Cache {
private Cache delegate;
public SynchronizedCache(Cache delegate) {
this.delegate = delegate;
}
@Override
public synchronized void putObject(Object key, Object object) {
delegate.putObject(key, object);
}
// 省略部分代碼
}
SoftCache
軟引用回收策略,軟引用只有當內存不足時才會被垃圾收集器回收。這里的實現機制中,使用了一個鏈表來保證一定數量的值即使內存不足也不會被回收,但是沒有保存在該鏈表的值則有可能會被回收。
在WeakHashMap中,可以看到是將引用應用到Key的,當Key被回收后,則移除相關的Value。但是這里是將其應用到Value中,因為Key不能被回收,如果被移除的話,就會影響到整個體系,最底層的實現使用HashMap實現的,沒有Key,就沒有辦法移除相關的值。反過來,值被回收了,將軟引用對象放到隊列中,可以根據Key調用removeObject移除該關聯的鍵和軟引用對象。
public class SoftCache implements Cache {
// 用於保存一定數量強引用的值
private final LinkedList<Object> hardLinksToAvoidGarbageCollection;
// 引用隊列,當被垃圾收集器回收時,會將軟引用對象放入此隊列
private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
private final Cache delegate;
// 保存強引用值的數量
private int numberOfHardLinks;
public SoftCache(Cache delegate) {
this.delegate = delegate;
this.numberOfHardLinks = 256;
this.hardLinksToAvoidGarbageCollection = new LinkedList<Object>();
this.queueOfGarbageCollectedEntries = new ReferenceQueue<Object>();
}
@Override
public void putObject(Object key, Object value) {
// 移除被垃圾收集器回收的鍵值
removeGarbageCollectedItems();
// 將軟件用作用到Value中
delegate.putObject(key, new SoftEntry(key, value, queueOfGarbageCollectedEntries));
}
@Override
public Object getObject(Object key) {
Object result = null;
@SuppressWarnings("unchecked")
SoftReference<Object> softReference = (SoftReference<Object>) delegate.getObject(key);
if (softReference != null) {
result = softReference.get();
if (result == null) {
// 該值被垃圾收集器回收,移除掉該項
delegate.removeObject(key);
} else {
// 這里以及下面的clear,想不通為什么要加hardLinksToAvoidGarbageCollection的同步?(在WeakCache中卻沒有加同步)
synchronized (hardLinksToAvoidGarbageCollection) {
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() {
synchronized (hardLinksToAvoidGarbageCollection) {
// 這里需要清空該隊列,否則即使下面調用clear,其Map清空了,但是部分值保留有引用,垃圾收集器也不會回收,會造成短暫的內存泄漏。
hardLinksToAvoidGarbageCollection.clear();
}
removeGarbageCollectedItems();
delegate.clear();
}
private void removeGarbageCollectedItems() {
SoftEntry sv;
// 清空被垃圾收集器回收的value其相關聯的鍵以及軟引用
while ((sv = (SoftEntry) queueOfGarbageCollectedEntries.poll()) != null) {
delegate.removeObject(sv.key);
}
}
// 這里軟引用對象是用在value中的
private static class SoftEntry extends SoftReference<Object> {
// 保存與value相關聯的Key,因為一旦被垃圾收集器回收,則此軟引用對象會被放到關聯的引用隊列中,這樣就可以根據Key,移除該鍵值對。
private final Object key;
private SoftEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {
super(value, garbageCollectionQueue);
this.key = key;
}
}
// 省略部分代碼
}
WeakCache
弱引用回收策略,弱引用的對象一旦被垃圾收集器發現,則會被回收,無論內存是否足夠。這里的實現和上面的軟引用類似,除了使用WeakReference替換掉SoftReference,其它基本一樣。還有一點想不通的就是,為什么SoftCache加鎖了,而這里沒有加鎖。
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;
}
TransactionalCache
事務緩存,在提交的時候,才真正的放到Cache中,或者回滾的時候清除掉,對Cache沒有影響。
public class TransactionalCache implements Cache {
private Cache delegate;
private boolean clearOnCommit;
private Map<Object, AddEntry> entriesToAddOnCommit;
private Map<Object, RemoveEntry> entriesToRemoveOnCommit;
public TransactionalCache(Cache delegate) {
this.delegate = delegate;
this.clearOnCommit = false;
this.entriesToAddOnCommit = new HashMap<Object, AddEntry>();
this.entriesToRemoveOnCommit = new HashMap<Object, RemoveEntry>();
}
@Override
public Object getObject(Object key) {
if (clearOnCommit) return null; // issue #146
return delegate.getObject(key);
}
@Override
public void putObject(Object key, Object object) {
// 移除當前事務中 待移除鍵值對操作
entriesToRemoveOnCommit.remove(key);
// 添加當前事務中 待增加到緩存鍵值對操作
entriesToAddOnCommit.put(key, new AddEntry(delegate, key, object));
}
@Override
public Object removeObject(Object key) {
// 移除增加該鍵值對的操作
entriesToAddOnCommit.remove(key);
// 添加移除鍵值對操作
entriesToRemoveOnCommit.put(key, new RemoveEntry(delegate, key));
return delegate.getObject(key);
}
@Override
public void clear() {
reset();
clearOnCommit = true;
}
public void commit() {
if (clearOnCommit) {
// 當提交事務時需要先清空,則清空緩存
delegate.clear();
} else {
// 應用移除鍵值對操作
for (RemoveEntry entry : entriesToRemoveOnCommit.values()) {
entry.commit();
}
}
// 應用添加鍵值對操作
for (AddEntry entry : entriesToAddOnCommit.values()) {
entry.commit();
}
reset();
}
public void rollback() {
reset();
}
private void reset() {
clearOnCommit = false;
entriesToRemoveOnCommit.clear();
entriesToAddOnCommit.clear();
}
private static class AddEntry {
private Cache cache;
private Object key;
private Object value;
public AddEntry(Cache cache, Object key, Object value) {
this.cache = cache;
this.key = key;
this.value = value;
}
public void commit() {
// 提交的時候,才真正放入緩存
cache.putObject(key, value);
}
}
private static class RemoveEntry {
private Cache cache;
private Object key;
public RemoveEntry(Cache cache, Object key) {
this.cache = cache;
this.key = key;
}
public void commit() {
// 提交的時候才真正從緩存中移除
cache.removeObject(key);
}
}
}
SerializedCache
序列化功能,將值序列化后存到緩存中。該功能用於緩存返回一份實例的Copy,用於保存線程安全。
public class SerializedCache implements Cache {
// 省略部分代碼
@Override
public void putObject(Object key, Object object) {
if (object == null || object instanceof Serializable) {
// 先序列化后再存放到緩存中
delegate.putObject(key, serialize((Serializable) object));
} else {
throw new CacheException("SharedCache failed to make a copy of a non-serializable object: " + object);
}
}
@Override
public Object getObject(Object key) {
Object object = delegate.getObject(key);
// 不為空,則反序列化,生成一份Copy
return object == null ? null : deserialize((byte[]) object);
}
private byte[] serialize(Serializable value) {
try {
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(value);
oos.flush();
oos.close();
return bos.toByteArray();
} catch (Exception e) {
throw new CacheException("Error serializing object. Cause: " + e, e);
}
}
private Serializable deserialize(byte[] value) {
Serializable result;
try {
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(value);
ObjectInputStream ois = new CustomObjectInputStream(bis);
result = (Serializable) ois.readObject();
ois.close();
} catch (Exception e) {
throw new CacheException("Error deserializing object. Cause: " + e, e);
}
return result;
}
public static class CustomObjectInputStream extends ObjectInputStream {
public CustomObjectInputStream(InputStream in) throws IOException {
super(in);
}
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
// 此方法只有在待序列化的類第一次序列化的時候才會被調用
// 遍歷所支持的ClassLoader,加載對應的Class
return Resources.classForName(desc.getName());
}
}
}
