Cache
- MyBatis 中的缓存是两层结构的,分为一级缓存、二级缓存,但在本质上是相同的,它们使用的都是 Cache 接口的实现。
- Cache 使用了装饰器模式,为 Cache 装饰了多个功能。
- 一级缓存是 PerpetualCache 对象,二级缓存是在 PerpetualCache 基础上装饰了其他功能。
Cache 接口
public interface Cache {
/**
* 该缓存对象的Id
* @return The identifier of this cache
*/
String getId();
/**
* 想缓存中存入数据
* @param key Can be any object but usually it is a {@link CacheKey}
* @param value The result of a select.
*/
void putObject(Object key, Object value);
/**
* 根据指定的 key 查找结果
* @param key The key
* @return The object stored in the cache.
*/
Object getObject(Object key);
/**
* 删除 key 的缓存
* As of 3.3.0 this method is only called during a rollback
* for any previous value that was missing in the cache.
* This lets any blocking cache to release the lock that
* may have previously put on the key.
* A blocking cache puts a lock when a value is null
* and releases it when the value is back again.
* This way other threads will wait for the value to be
* available instead of hitting the database.
*
*
* @param key The key
* @return Not used
*/
Object removeObject(Object key);
/**
* 清空缓存
* Clears this cache instance.
*/
void clear();
/**
* 缓存对象个数
* Optional. This method is not called by the core.
*
* @return The number of elements stored in the cache (not its capacity).
*/
int getSize();
/**
* 获取读写锁
* Optional. As of 3.2.6 this method is no longer called by the core.
* <p>
* Any locking needed by the cache must be provided internally by the cache provider.
*
* @return A ReadWriteLock
*/
default ReadWriteLock getReadWriteLock() {
return null;
}
}
PerpetualCache
PerpetualCache 永久的缓存,一级缓存(LocalCache)和二级缓存的默认缓存。内部通过 HashMap 对象进行数据缓存,并重写了 equals 和 hashCode 方法。
public class PerpetualCache implements Cache {
private final String id;
private Map<Object, Object> cache = new HashMap<>();
public PerpetualCache(String id) {
this.id = id;
}
@Override
public String getId() {
return id;
}
@Override
public int getSize() {
return cache.size();
}
@Override
public void putObject(Object key, Object value) {
cache.put(key, value);
}
@Override
public Object getObject(Object key) {
return cache.get(key);
}
@Override
public Object removeObject(Object key) {
return cache.remove(key);
}
@Override
public void clear() {
cache.clear();
}
@Override
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());
}
@Override
public int hashCode() {
if (getId() == null) {
throw new CacheException("Cache instances require an ID.");
}
return getId().hashCode();
}
}
LruCache
最近最少使用算法
LinkedHashMap.accessOrder 参数为 true,代表访问顺序,为 false 代表插入顺序。
public class LruCache implements Cache {
private final Cache delegate;
// LinkedHashMap<Object,Object> 类型对象,它是一个有序的 HashMap,用于记录 key 最近的使用情况
private Map<Object, Object> keyMap;
// 记录最少被使用的缓存项的 key
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();
}
/** 重新设置缓存大小
*
* https://colobu.com/2015/09/07/LRU-cache-implemented-by-Java-LinkedHashMap/
* https://juejin.im/post/6844903917524893709
*/
public void setSize(final int size) {
// 重新设置缓存大小,会重置 keyMap 字段
// true 参数 代表访问顺序,LinkedHashMap.get(),会改变其记录的顺序
keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
private static final long serialVersionUID = 4267176411845948333L;
// 当调用 LinkedHashMap.put() 方法时,会调用该方法
@Override
protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
boolean tooBig = size() > size;
if (tooBig) {
// 如果已达到缓存上限,则更新 eldestKey 字段,后面会删除该项
eldestKey = eldest.getKey();
}
return tooBig;
}
};
}
/**
* 存入缓存
* @param key Can be any object but usually it is a {@link CacheKey}
* @param value The result of a select.
*/
@Override
public void putObject(Object key, Object value) {
delegate.putObject(key, value);
// 删除最久为使用缓存项
cycleKeyList(key);
}
@Override
public Object getObject(Object key) {
// 修改 LinkedHashMap 中记录的顺序
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();
}
/**
*
* @param key
*/
private void cycleKeyList(Object key) {
keyMap.put(key, key);
if (eldestKey != null) {
delegate.removeObject(eldestKey);
eldestKey = null;
}
}
}
FifoCache
先进先出缓存,使用 LinkedList 来实现先进先出
public class FifoCache implements Cache {
private final Cache delegate;
// 用于记录 key 进入缓存先后顺序,使用的是 LinkedList<Object> 类型的集合对象
private final Deque<Object> keyList;
private int size;
public FifoCache(Cache delegate) {
this.delegate = delegate;
this.keyList = new LinkedList<>();
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();
}
private void cycleKeyList(Object key) {
keyList.addLast(key);
if (keyList.size() > size) {
Object oldestKey = keyList.removeFirst();
delegate.removeObject(oldestKey);
}
}
}
网友评论