一、概述
MyBatis中的缓存是两层结构的,分为一级缓存、二级缓存,但在本质上是相同的,它们使用的都是Cache接口的实现。
二、装饰器模式简介
Mybatis缓存模块使用了装饰模式。这里简单介绍一下装饰模式,该模式主要实现动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。(引自《大话设计模式》)
装饰器模式的结构图如下:
其中,Component:对象接口,也可以是一个抽象类,是被装饰类和装饰类的基本类型,可以给这些对象动态地添加职责。
ConcreteComponent:被装饰类,Component的实现类,是要被进行修饰的对象,也可以给这个对象动态地添加职责。可以有多个。
Decorator:装饰类,是一个抽象类,实现了Component,同时,在内部维护了一个ConcreteComponent的实例,可以通过构造函数进行初始化。在其内部只是声明了一个个装饰的方法,具体实现由其子类去实现。
ConcreteDecoratorA/ConcreteDecoratorB:具体的装饰产品类,继承了Decorator,其中可以通过构造器声明装饰哪种类型的ConcreteComponent。还包括装饰的具体实现方法,起到了给Component添加职责的功能。
三、类结构
Mybatis的缓存模块的包目录:org.apache.ibatis.cache。具体包结构如下图所示:
其中,包括了Cache接口,子目录impl下的实现类PerpetualCache,子目录decorators装饰类**Cache等。下面分别介绍各个模块:
1、Cache
Cache接口是缓存模块中最核心的接口,它定义了所有缓存的基础方法。代码如下所示:
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();
}
2、PerpetualCache
Mybatis的Cache实现类,通过HashMap进行缓存。
- 字段、构造函数
在实现类PerpetualCache中包含id、cache 和一个带参构造函数。
- id
缓存对象的ID - cache
存储缓存项的Map对象 - 构造函数
初始化对象,给字段id赋值。
private final String id;
private Map<Object, Object> cache = new HashMap<Object, Object>();
public PerpetualCache(String id) {
this.id = id;
}
@Override
public String getId() {
return id;
}
- 常用方法
实现类中的方法,都是基于cache对象,进行操作的,比较简单,这里不再具体分析。
@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 ReadWriteLock getReadWriteLock() {
return null;
}
@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();
}
3、CacheKey
缓存一般通过key-value格式存储,CacheKey一般用于表示key。在一个CacheKey对象中可以封装多个影响缓存项的因素。
- 字段、构造函数
这些字段,主要用来保证CacheKey对象的唯一性。其中multiplier参与计算hashcode ,默认位是37;hashcode是CacheKey对象的hashcode ,初始位是17;checksum表示校验和;count表示updateList 集合的个数;updateList表示对象集合,并由该集合中的所有对象参与决定两个CacheKey是否相同。构造函数,主要实现实例的初始化和字段的初始化。
public static final CacheKey NULL_CACHE_KEY = new NullCacheKey();
private static final int DEFAULT_MULTIPLYER = 37;
private static final int DEFAULT_HASHCODE = 17;
private final int multiplier;
private int hashcode;
private long checksum;
private int count;
private List<Object> updateList;
public CacheKey() {
this.hashcode = DEFAULT_HASHCODE;
this.multiplier = DEFAULT_MULTIPLYER;
this.count = 0;
this.updateList = new ArrayList<Object>();
}
- 主要方法
- update()
向updateList集合中添加对象时,使用的是CacheKey.update()方法。添加对象的同时,重新计算相关字段。代码如下:
public void update(Object object) {
int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);
count++;
checksum += baseHashCode;
baseHashCode *= count;
hashcode = multiplier * hashcode + baseHashCode;
updateList.add(object);
}
- equals()
重写了equals()方法,重新定义了CacheKey对象是否相等的条件。
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (!(object instanceof CacheKey)) {
return false;
}
final CacheKey cacheKey = (CacheKey) object;
if (hashcode != cacheKey.hashcode) {
return false;
}
if (checksum != cacheKey.checksum) {
return false;
}
if (count != cacheKey.count) {
return false;
}
for (int i = 0; i < updateList.size(); i++) {
Object thisObject = updateList.get(i);
Object thatObject = cacheKey.updateList.get(i);
if (!ArrayUtil.equals(thisObject, thatObject)) {
return false;
}
}
return true;
}
4、FifoCache
FifoCache是Cache的先入先出的装饰器, 当向缓存添加数据时,如果缓存项的个数已经达到上限, 则会将缓存中最早进入缓存的缓存项删除。
- 字段、构造函数
FifoCache定义了三个字段,其中delegate表示被装饰的Cache对象,keyList用于存储key进入缓存的先后顺序, 使用的是Deque<Object>类型的集合对象,size表示了缓存项的上限, 超过该值, 则需要清理最早缓存的缓存项;构造函数主要实现对象的初始化和字段的赋值工作。
private final Cache delegate;
private final Deque<Object> keyList;
private int size;
public FifoCache(Cache delegate) {
this.delegate = delegate;
this.keyList = new LinkedList<Object>();
this.size = 1024;
}
- 主要方法
装饰器中的方法,基础方法都还是调用被装饰对象的方法,只是在这基础上增加了需要增强的功能。其中,cycleKeyList()方法实现了移除最早存储对象的功能,所以在需要添加存储项的时候,需要执行该方法。
@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);
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/68906.html