概述
缓存包是mybatis对缓存的抽象和实现,可为其他模块提供缓存的支持。其设计也很值得借鉴。
大概分为
- 缓存定义(抽象、异常、缓存键)
- 缓存实现
- 缓存装饰者
可以重点在于缓存策略装饰模式实现,可以复习复习装饰模式啦。
Cache
public interface Cache {
String getId();
void putObject(Object key, Object value);
Object getObject(Object key);
Object removeObject(Object key);
void clear();
int getSize();
}
缓存容器基础抽象,定义了基础的缓存操作。
所有缓存容器实现类。其中只有PerpetualCache是真正的缓存实现类,其余都是装饰实现类。
PerpetualCache
永不过期缓存容器,实现比较简单,最基础的单机缓存容器实现。
public class PerpetualCache implements Cache {
// 缓存容器id
private final String id;
// 缓存容器载体
private Map<Object, Object> cache = new HashMap<>();
}
缓存操作基于HashMap,非常简单。
Decorator
decorator有一个单独的子包,全部放着缓存的装饰类。
- BlockingCache
- FifoCache
- loggingCache
- LruCache
- ScheduledCache
- SerializedCache
- SoftCache
- SynchronizedCache
- TransactionalCache
- WeakCache
通过名字可以很容易就辨认出该实现的作用。
让我们先复习下装饰模式
这是装饰模式的类图
Component:抽象组件
ConcreteComponent:组件实现类
Decorator:装饰实现类,实现抽象组件,组合ConcreteComponent,并在其上额外添加功能。
好处是添加功能时无需对之前类进行修改,职责也更加清晰。
让我们来看看mybatis缓存中Decorator的实现。
FifoCache
public class FifoCache implements Cache {
private final Cache delegate;
/**
* 双端队列
*/
private final Deque<Object> keyList;
/**
* 固定大小
*/
private int size;
public FifoCache(Cache delegate) {
this.delegate = delegate;
this.keyList = new LinkedList<>();
this.size = 1024;
}
}
设置初始缓存大小,使用双端队列进行控制key大小。实现也比较简单。
WeakCache
虚引用缓存,缓存的值使用虚引用包装。
又被迫复习下虚引用的知识,gc时如果发现内存不足会清楚虚引用包裹的对象。
public class WeakCache implements Cache {
// 双端队列保存一定数量的值,避免被gc
private final Deque<Object> hardLinksToAvoidGarbageCollection;
// 引用队列,被gc清楚的值会放在里面
private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
private final Cache delegate;
// 队列大小
private int numberOfHardLinks;
public WeakCache(Cache delegate) {
this.delegate = delegate;
this.numberOfHardLinks = 256;
this.hardLinksToAvoidGarbageCollection = new LinkedList<>();
this.queueOfGarbageCollectedEntries = new ReferenceQueue<>();
}
@Override
public String getId() {
return delegate.getId();
}
@Override
public int getSize() {
removeGarbageCollectedItems();
return delegate.getSize();
}
public void setSize(int size) {
this.numberOfHardLinks = size;
}
@Override
public void putObject(Object key, Object value) {
removeGarbageCollectedItems();
delegate.putObject(key, new WeakEntry(key, value, queueOfGarbageCollectedEntries));
}
@Override
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;
}
@Override
public Object removeObject(Object key) {
removeGarbageCollectedItems();
return delegate.removeObject(key);
}
@Override
public void clear() {
hardLinksToAvoidGarbageCollection.clear();
removeGarbageCollectedItems();
delegate.clear();
}
private void removeGarbageCollectedItems() {
WeakEntry sv;
while ((sv = (WeakEntry) queueOfGarbageCollectedEntries.poll()) != null) {
delegate.removeObject(sv.key);
}
}
private static class WeakEntry extends WeakReference<Object> {
private final Object key;
private WeakEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {
super(value, garbageCollectionQueue);
this.key = key;
}
}
}
每次操作时调用removeGarbageCollectedItems()方法,用于清楚已经被gc清除了的缓存值。
其他
还有一些策略缓存装饰几乎使用的很少,参考参考即可。
总结
缓存包 比较清晰简单,但是有借鉴的地方的。在我们业务开发时,可以也为应用设计一个独立的缓存包,后续甚至可以独立为服务,一些缓存策略也可以使用装饰模式来实现。代码肯定会比直接操作redis清晰许多。
That’s All