Cache接口简介
org.apache.ibatis.cache.Cache是MyBatis的缓存接口,想要实现自定义的缓存需要实现这个接口。
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();
}
MyBatis中关于Cache接口的实现类也使用了装饰者设计模式。
LRU – 最近最少使用的:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
实现
PerpetualCache缓存
是基础缓存类,通过装饰器模式来实现不同的缓存。比如FIFO类型和LRU等缓存。
源码如下
public class PerpetualCache implements Cache {
private String id;
private Map<Object, Object> cache = new HashMap<Object, Object>();
public PerpetualCache(String id) {
this.id = id;
}
public String getId() {
return id;
}
public int getSize() {
return cache.size();
}
public void putObject(Object key, Object value) {
cache.put(key, value);
}
public Object getObject(Object key) {
return cache.get(key);
}
public Object removeObject(Object key) {
return cache.remove(key);
}
public void clear() {
cache.clear();
}
public ReadWriteLock getReadWriteLock() {
return null;
}
public boolean equals(Object o) {
if (getId() == null) throw new CacheException("Cache instances require an ID.");
if (this == o) return true;
if (!(o instanceof Cache)) return false;
Cache otherCache = (Cache) o;
return getId().equals(otherCache.getId());
}
public int hashCode() {
if (getId() == null) throw new CacheException("Cache instances require an ID.");
return getId().hashCode();
}
}
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;
setSize(1024);
}
@Override
public String getId() {
return delegate.getId();
}
@Override
public int getSize() {
return delegate.getSize();
}
public void setSize(final int size) {
keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
private static final long serialVersionUID = 4267176411845948333L;
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);
cycleKeyList(key);
}
@Override
public Object getObject(Object key) {
keyMap.get(key); //touch
return delegate.getObject(key);
}
@Override
public Object removeObject(Object key) {
return delegate.removeObject(key);
}
@Override
public void clear() {
delegate.clear();
keyMap.clear();
}
public ReadWriteLock getReadWriteLock() {
return null;
}
private void cycleKeyList(Object key) {
keyMap.put(key, key);
if (eldestKey != null) {
delegate.removeObject(eldestKey);
eldestKey = null;
}
}
}
cycleKeyList方法中 keyMap.put(key, key);会调用LinkedHashedMap中的removeEldestEntry方法,若超过设定大小,则会设置eldestKey。
最近最少使用是基于LinkedHashedMap LRU的原则。
void addEntry(int hash, K key, V value, int bucketIndex) {
//创建新的Entry,并插入到LinkedHashMap中
createEntry(hash, key, value, bucketIndex); // 重写了HashMap中的createEntry方法
//双向链表的第一个有效节点(header后的那个节点)为最近最少使用的节点,这是用来支持LRU算法的
Entry<K,V> eldest = header.after;
//如果有必要,则删除掉该近期最少使用的节点,
//这要看对removeEldestEntry的覆写,由于默认为false,因此默认是不做任何处理的。
if (removeEldestEntry(eldest)) {
removeEntryForKey(eldest.key);
} else {
//扩容到原来的2倍
if (size >= threshold)
resize(2 * table.length);
}
}
FIFO缓存
使用装饰器设计模型,。封装了Cache。
public class FifoCache implements Cache {
private final Cache delegate;
private LinkedList<Object> keyList;
private int size;
public FifoCache(Cache delegate) {
this.delegate = delegate;
this.keyList = new LinkedList<Object>();
this.size = 1024;
}
@Override
public String getId() {
return delegate.getId();
}
@Override
public int getSize() {
return delegate.getSize();
}
public void setSize(int size) {
this.size = size;
}
@Override
public void putObject(Object key, Object value) {
cycleKeyList(key);
delegate.putObject(key, value);
}
@Override
public Object getObject(Object key) {
return delegate.getObject(key);
}
@Override
public Object removeObject(Object key) {
return delegate.removeObject(key);
}
@Override
public void clear() {
delegate.clear();
keyList.clear();
}
@Override
public ReadWriteLock getReadWriteLock() {
return null;
}
private void cycleKeyList(Object key) {
keyList.addLast(key);
if (keyList.size() > size) {
Object oldestKey = keyList.removeFirst();
delegate.removeObject(oldestKey);
}
}
}
默认的缓存数量大小size是1024。每次超过这个size。就会使用缓存过期处理方法cycleKeyList().
在方法cycleKeyList中,会先删除先进来的Key。
在FIFO中使用LinkedList 即链表结构的List来保存Key。来达到先进先删除的目的。新增加的Key都会保存在链表末尾。
linkedHashedMap代码
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMap.Entry<K,V> last;
if (accessOrder && (last = tail) != e) {
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a != null)
a.before = b;
else
last = b;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}
从上面的方法可以看出,要实现LRU需要重写removeEldestEntry方法,且需要设置及accessOrder=true(这个参数在创建Map的时候可以设置),这样每次元素被访问之后就会被放在链表最末尾,删除的时候会将链表中的第一个元素删除。
定时缓存-ScheduledCache
源码如下:
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 String getId() {
return delegate.getId();
}
@Override
public int getSize() {
clearWhenStale();
return delegate.getSize();
}
@Override
public void putObject(Object key, Object object) {
clearWhenStale();
delegate.putObject(key, object);
}
@Override
public Object getObject(Object key) {
if (clearWhenStale()) {
return null;
} else {
return delegate.getObject(key);
}
}
@Override
public Object removeObject(Object key) {
clearWhenStale();
return delegate.removeObject(key);
}
@Override
public void clear() {
lastClear = System.currentTimeMillis();
delegate.clear();
}
@Override
public ReadWriteLock getReadWriteLock() {
return null;
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public boolean equals(Object obj) {
return delegate.equals(obj);
}
private boolean clearWhenStale() {
if (System.currentTimeMillis() - lastClear > clearInterval) {
clear();
return true;
}
return false;
}
从源码可以看出,定时缓存并没有开启一个定时任务来定期清除缓存,只是操作(查找,查询等)时候调用clearWhenStale()方法来清除,这里清除的是缓存中所有数据。
同步Cache
public class SynchronizedCache implements Cache {
private Cache delegate;
public SynchronizedCache(Cache delegate) {
this.delegate = delegate;
}
@Override
public String getId() {
return delegate.getId();
}
@Override
public synchronized int getSize() {
return delegate.getSize();
}
@Override
public synchronized void putObject(Object key, Object object) {
delegate.putObject(key, object);
}
@Override
public synchronized Object getObject(Object key) {
return delegate.getObject(key);
}
@Override
public synchronized Object removeObject(Object key) {
return delegate.removeObject(key);
}
@Override
public synchronized void clear() {
delegate.clear();
}
....
同步cache只是在操作方法上增加的Synchronized关键字。来达到同步的目的。
事务缓存TransactionalCache
源码如下:
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 String getId() {
return delegate.getId();
}
@Override
public int getSize() {
return delegate.getSize();
}
@Override
public Object getObject(Object key) {
if (clearOnCommit) return null; // issue #146
return delegate.getObject(key);
}
@Override
public ReadWriteLock getReadWriteLock() {
return null;
}
@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);
}
}
}
从源码可以看出,在源码中多了两个Map,entriesToAddOnCommit和entriesToRemoveOnCommit,当调用put插入和remove删除时候,这是操作这两个对象,只是当真正调用commit时候,才将内容保存到实际的Cache中。