Mybatis默认的缓存接口是Cache,默认的实现是PerpetualCache,有一个HashMap的成员变量:
private Map cache =new HashMap<>();
所有的put和get都是往cache这个成员变量中操作,每个操作没有加锁,是个线程不安全的。
Mybatis还提供了几个修饰类作为缓存:
BlockingCache,FIFOCache,LoggingCache,LruCache,ScheduleCache,SerialezedCache,
SoftCache,SynchronizedCache,TransactionalCache,WeakCache。
BlockingCache
BlockingCache实现了Cache接口拥有一个Cache成员变量和一个锁的map
private final Cache delegate;
private final ConcurrentHashMap locks;
看get方法:
public Object getObject(Object key) {//取的时候加锁
acquireLock(key);
Object value =delegate.getObject(key);
if (value !=null) {
releaseLock(key);
}
return value;
}
可以看出getObject的时候会加锁
FifoCache
借助private final Deque keyList这个双向队列
put方法
public void putObject(Object key, Object value) {
cycleKeyList(key);
delegate.putObject(key, value);
}
private void cycleKeyList(Object key) {
keyList.addLast(key);
if (keyList.size() >size) {
Object oldestKey =keyList.removeFirst();
delegate.removeObject(oldestKey);
}
}
put的时候判断队列的大小是否大于给定的值1024,如果大于删除头结点,并从缓存中删除对应的key
LoggingCache
每次get的时候提供日志打印
@Override
public Object getObject(Object key) {
requests++;
final Object value =delegate.getObject(key);
if (value !=null) {
hits++;
}
if (log.isDebugEnabled()) {
log.debug("Cache Hit Ratio [" + getId() +"]: " + getHitRatio());
}
return value;
}
LruCache
借助于private Map keyMap;实现为LinkedHashMap
keyMap =new LinkedHashMap(size,.75F,true) {
private static final long serialVersionUID =4267176411845948333L;
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
boolean tooBig = size() >size;
if (tooBig) {
eldestKey = eldest.getKey();
}
return tooBig;
}
};
removeEldestEntry为LinkedHahMap实现LRU算法的实现
put方法
public void putObject(Object key, Object value) {
delegate.putObject(key, value);
cycleKeyList(key);
}
private void cycleKeyList(Object key) {
keyMap.put(key, key);
if (eldestKey !=null) {
delegate.removeObject(eldestKey);
eldestKey =null;
}
}
每次putObject的时候查询是否有eldestKey ,有的话从缓存删除
ScheduledCache
有两个成员变量清除间隔clearInterval和上次清除时间lastClear
每次有getSize,getObject和putObject的时候,调用clearWhenStale
private boolean clearWhenStale() {
if (System.currentTimeMillis() -lastClear >clearInterval) {
clear();
return true;
}
return false;
}
判断是否到达清除间隔时间,到达的话调用clear,清除所有的缓存信息
@Override
public void clear() {
lastClear = System.currentTimeMillis();
delegate.clear();
}
SerializedCache
需要存入的对象实现Serializable接口
put方法:
@Override
public void putObject(Object key, Object object) {
if (object ==null || objectinstanceof Serializable) {
delegate.putObject(key, serialize((Serializable) object));
}else {
throw new CacheException("SharedCache failed to make a copy of a non-serializable object: " + 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);
}
}
JAVA原生的序列化方式序列化后存入缓存
get的方法如下:
@Override
public Object getObject(Object key) {
Object object =delegate.getObject(key);
return object ==null ?null : deserialize((byte[]) object);
}
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;
}
调用JAVA原生的反序列化方式反序列化出缓存的对象
SoftCache
借助双向队列hardLinksToAvoidGarbageCollection和ReferenceQueue实例queueOfGarbageCollectedEntries
以及一个软引用的子类SoftEntry
private static class SoftEntryextends SoftReference {
private final Objectkey;
SoftEntry(Object key, Object value, ReferenceQueue garbageCollectionQueue) {
super(value, garbageCollectionQueue);
this.key = key;
}
}
put的方法如下:
@Override
public void putObject(Object key, Object value) {
removeGarbageCollectedItems();
delegate.putObject(key,new SoftEntry(key, value,queueOfGarbageCollectedEntries));
}
把每个value变为一个软引用
removeGarbageCollectedItems:
private void removeGarbageCollectedItems() {
SoftEntry sv;
while ((sv = (SoftEntry)queueOfGarbageCollectedEntries.poll()) !=null) {
delegate.removeObject(sv.key);
}
}