mybatis基础支持层-缓存模块
一、装饰器模式
- 使用场景:需要动态的为对象添加功能,基于组合的方式实现(而不是继承,组合优于继承),保证已有组件的稳定性
- 角色:
- Component 组件: 接口,定义了全部组件实现类以及所有装饰器实现的行为
- ConcreteComponent 具体组件实现类 : 实现了Component接口,实现基础功能的实现,高级功能是通过装饰器方式添加的
- Decorator 装饰器:所有装饰器的父类,是一个实现了component接口的抽象类,其中封装了Component对象,也是被装饰的对象。而这个被装饰者只需要Component对象即可,实现了装饰器的组合和复用
- ConcreteDecorator: 具体的装饰器实现类,主要是为Component对象添加功能
- 例子:FileInputStream 没有缓冲的功能,然后BufferedInputStream就是提供缓冲功能的装饰器,每次read的时候从文件中预获取一部分数据到缓冲区,可以减少用户态和内核态的切换,提高性能
- mybatis例子: mybatis中的装饰器,将Decoretor和Component接口合为一体
- 装饰器的优点:装饰器采用组合的形式,更为灵活,可扩展性强,能够创建不同的组件满足不同的需求,保障了基础组件的稳定性,符合“开放-封闭”原则
二、Cache接口
- Cache是顶级接口,涵盖getId、putObject、getObject、removeObject、getSize、getReadWriteLock,比较重要的就是put get remove的方法
- PerpetualCache 实现Cache的接口,使用HashMap实现缓存,使用map的put get remove来Cache中的方法,担当ConcreteComponent角色
-
对于其他装饰类,主要是丰富PerpetualCache类的功能
- BlockingCache 内含Cache接口 阻塞版本的装饰器,保证只有一个线程到数据库中查找指定key的数据
- BlockingCache 获取缓存数据
@Override
public Object getObject(Object key) {
//尝试获取锁
acquireLock(key);
//获取锁成功
Object value = delegate.getObject(key);
//获取内容
if (value != null) {
//释放锁
releaseLock(key);
}
return value;
}
private void acquireLock(Object key) {
Lock lock = getLockForKey(key);
if (timeout > 0) {
//加锁超时
try {
//带超时版本的加锁
boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS);
if (!acquired) {
throw new CacheException("Couldn't get a lock in " + timeout + " for the key " + key + " at the cache " + delegate.getId());
}
} catch (InterruptedException e) {
throw new CacheException("Got interrupted while trying to acquire lock for key " + key, e);
}
} else {
//直接加锁
lock.lock();
}
}
private ReentrantLock getLockForKey(Object key) {
//直接获取锁,如果还没有加锁就生成
return locks.computeIfAbsent(key, k -> new ReentrantLock());
}
- BlockingCache 添加缓存数据
@Override
public void putObject(Object key, Object value) {
try {
delegate.putObject(key, value);
} finally {
releaseLock(key);
}
}
- FifoCache 先进先出Deque修饰keys的缓存装饰器,添加缓存的时候,如果缓存数量大设置,则会删除最早的缓存。get yu remove的方法直接调用Cache的get和remove方法
@Override
public void putObject(Object key, Object value) {
cycleKeyList(key);
delegate.putObject(key, value);
}
private void cycleKeyList(Object key) {
//添加key
keyList.addLast(key);
//key数量大于设置
if (keyList.size() > size) {
//删除第一个缓存
Object oldestKey = keyList.removeFirst();
delegate.removeObject(oldestKey);
}
}
- LruCache 最近最少使用修饰keys的缓存装饰器,清理缓存的时候它会清除最近最少使用的项。使用new LinkedHashMap<Object, Object>(size, .75F, true)的基本原理来实现最近最少使用的语义。get和put方法会做少许修饰操作,remove方法直接使用Cache的remove
// 初始化的时候初始化Map
public LruCache(Cache delegate) {
this.delegate = delegate;
setSize(1024);
}
public void setSize(final int size) {
keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
private static final long serialVersionUID = 4267176411845948333L;
@Override
protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
boolean tooBig = size() > size;
if (tooBig) {
eldestKey = eldest.getKey();
}
return tooBig;
}
};
}
//添加put
@Override
public void putObject(Object key, Object value) {
//放入缓存
delegate.putObject(key, value);
// 对放入的缓存做特殊处理
cycleKeyList(key);
}
private void cycleKeyList(Object key) {
//放入LRU MAP
keyMap.put(key, key);
//如果存在需要删除的key,就清除
if (eldestKey != null) {
delegate.removeObject(eldestKey);
eldestKey = null;
}
}
//获取值
@Override
public Object getObject(Object key) {
//LRU MAP 刷新使用 ,修改次序
keyMap.get(key); // touch
//Cache 获取值
return delegate.getObject(key);
}
三、SoftCache & WeakCache 主要通过软引用、弱引用实现
强引用、软引用、引用队列、弱引用、幽灵引用(虚引用)
1.强引用
- 场景: 通常使用的 P p = new P();
- 采用强引用,即使JVM内存空间不足,GC也不会回收该对象。因而当出现JVM内存不足情况,就会抛 OOM
2.软引用 SoftReference
- 使用SoftReference修饰,强度低于强引用
- 场景:当JVM内存不足时,会回收软引用对象,如果回收了软引用对象还是内存不足,才会抛出OOM。因而,软引用适用于可以通过恢复来还原的对象,比如一些临时缓存,可以通过数据库重新加载的,可以使用软引用来修饰
- 软引用的对象,需要通过Reference.get()的方法来确认对象是否被回收
3.引用队列 ReferenceQueue
- 场景: 用于对象可达性发生变化,引用队列就是用于收集这些信息的队列。例如SoftRerence 可以匹配一个ReferenceQueue, 当对象值被JVM回收后,还是能够读取ReferenceQueue获取对象信息
4.弱引用
- WeakReference修饰,比软引用强度弱
- 当JVM进行GC垃圾回收的时候,如果对象是弱引用修饰的,就会被回收掉,因而它的生命周期尽可能在两次JVM GC之间
- 典型case: java.util.WeakHashMap
5.幽灵引用(虚引用)
- PhantomReference修饰,最弱的引用类型
- 可以用于实现比较精细的内存控制
- 幽灵引用,必须指定一个引用队列,当GC准备回收一个对象,而对象存在幽灵引用的时候,会将该虚引用加入到与之关联的引用队列中
6.SoftCache
用到上面学习的ReferenceQueue
- 属性
//最近使用的缓存项,添加到 hardLinksToAvoidGarbageCollection ,能够避免被GC回收
private final Deque<Object> hardLinksToAvoidGarbageCollection;
//ReferenceQueue ,引用队列,用于记录已经被GC回收的SoftEntry对象
private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
private final Cache delegate;
//默认禁止GC回收的缓存项数量,默认256
private int numberOfHardLinks;
- putObject:主要关注一个无效key的清除,以及软引用绑定的引用队列的应用
@Override
public void putObject(Object key, Object value) {
//操作之前,都会清除一下value已被GC回收的引用
removeGarbageCollectedItems();
//放入缓存,value是被SoftEntry修饰的
delegate.putObject(key, new SoftEntry(key, value, queueOfGarbageCollectedEntries));
}
private void removeGarbageCollectedItems() {
SoftEntry sv;
//从value绑定的 ReferenceQueue中查找(如果value被回收,会将对象放在ReferenceQueue中)
while ((sv = (SoftEntry) queueOfGarbageCollectedEntries.poll()) != null) {
//如果value被回收,就删除对应的key
delegate.removeObject(sv.key);
}
}
//主要继承SoftReference,软引用的特性,指定了一个ReferenceQueue
private static class SoftEntry extends SoftReference<Object> {
private final Object key;
SoftEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {
super(value, garbageCollectionQueue);
this.key = key;
}
}
- getObject
@Override
public Object getObject(Object key) {
Object result = null;
@SuppressWarnings("unchecked") // assumed delegate cache is totally managed by this cache
//直接从Cache获取值
SoftReference<Object> softReference = (SoftReference<Object>) delegate.getObject(key);
if (softReference != null) {
//如果软引用存在
result = softReference.get();
if (result == null) {
//获取值为空,已被GC回收,清除对应key
delegate.removeObject(key);
} else {
//未被GC回收
// See #586 (and #335) modifications need more than a read lock
synchronized (hardLinksToAvoidGarbageCollection) {
//同步对象,添加结果,最近使用,不允许GC回收
hardLinksToAvoidGarbageCollection.addFirst(result);
//如果超过数量的限制
if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {
//清除最老的缓存项
hardLinksToAvoidGarbageCollection.removeLast();
}
}
}
}
return result;
}
- removeObject 比较简单,就是清除无效Key,并删除对应Key
@Override
public Object removeObject(Object key) {
removeGarbageCollectedItems();
return delegate.removeObject(key);
}
- clear
@Override
public void clear() {
synchronized (hardLinksToAvoidGarbageCollection) {
hardLinksToAvoidGarbageCollection.clear();
}
removeGarbageCollectedItems();
delegate.clear();
}
7. WeakCache
- 方法流程与SoftCache, 使用WeakEntry 替代SoftEntry ,使用 WeakReference 替换 SoftReference
8.ScheduledCache
周期性清理的装饰器
- 属性
private final Cache delegate;
//清理的周期
protected long clearInterval;
//最近一次清理的时间
protected long lastClear;
- putObject/getObject/removeObject 方法前都会调用clearWhenStale方法,如果满足清理时间天津,就清理Cache
9.LoggingCache
内含一个Log对象,用于记录getObject时候的日志
10.SynchronizedCache
针对Cache接口中的方法,都使用synchronized修饰,进行同步控制
11.SerializedCache
putObject 对value进行序列化,getObject 对value进行反序列化
四、CacheKey
- CacheKey是Cache中唯一标识一个Cache的标志,内含多项影响因素
- 内部主要用于确定两个Cache是否相同,提供update,updateAll,hashcode,equals,tostring,clone等方法