MyBatis缓存设计使用了装饰模式和责任链模式。本文将分析MyBatis缓存对象的结构和创建过程。
使用MyBatis缓存,首先要在mapper.xml文件中配置
/**
* 解析节点
* @param context 节点对象
* @throws Exception
*/
private void cacheElement(XNode context) throws Exception {
// 配置了节点
if (context != null) {
// 基础缓存的类型,可以自定义,默认为PERPETUAL
String type = context.getStringAttribute("type", "PERPETUAL");
// 加载类,如果配置错误,这里将抛找不到类异常
Class extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
// 缓存回收算法,默认为最少使用回收LRU
String eviction = context.getStringAttribute("eviction", "LRU");
// 同样去加载算法类
Class extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
// 缓存自动刷新时间
Long flushInterval = context.getLongAttribute("flushInterval");
// 缓存大小
Integer size = context.getIntAttribute("size");
// 是否只读
boolean readWrite = !context.getBooleanAttribute("readOnly", false);
// 其他配置属性
Properties props = context.getChildrenAsProperties();
// 创建缓存
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, props);
}
}
创建的过程使用建造者模式,通过CacheBuilder来创建Cache对象。创建结束后,将Cache添加都configuration对象,这是全局配置对象,存在整个MyBatis的生命周期中。
/**
* 创建新缓存容器
* @param typeClass 基础缓存容器
* @param evictionClass 缓存回收算法
* @param flushInterval 有效时间
* @param size 容器大小
* @param readWrite 是否只读
* @param props 其他属性
* @return 缓存容器
*/
public Cache useNewCache(Class extends Cache> typeClass,
Class extends Cache> evictionClass,
Long flushInterval,
Integer size,
boolean readWrite,
Properties props) {
// 如果为空设置默认值
typeClass = valueOrDefault(typeClass, PerpetualCache.class);
evictionClass = valueOrDefault(evictionClass, LruCache.class);
// 各个属性传入容器建造者,调用build创建缓存容器
Cache cache = new CacheBuilder(currentNamespace)
.implementation(typeClass)
.addDecorator(evictionClass)
.clearInterval(flushInterval)
.size(size)
.readWrite(readWrite)
.properties(props)
.build();
// 将缓存容器添加到全局配置对象的范围
configuration.addCache(cache);
// 将当前缓存容器到Mapper的创建助手
currentCache = cache;
return cache;
}
/**
* 创建缓存容器
* @return 缓存容器
*/
public Cache build() {
// 设置基础缓存容器,回收算法的默认实现
setDefaultImplementations();
// implementation是一个Class对象,通过反射创建缓存容器对象,并设置id
Cache cache = newBaseCacheInstance(implementation, id);
setCacheProperties(cache);
if (PerpetualCache.class.equals(cache.getClass())) { // issue #352, do not apply decorators to custom caches
for (Class extends Cache> decorator : decorators) {
// 回收算法容器
cache = newCacheDecoratorInstance(decorator, cache);
setCacheProperties(cache);
}
// 构造标准缓存链
cache = setStandardDecorators(cache);
} else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
// 日志容器
cache = new LoggingCache(cache);
}
// 返回一个缓存容器。
return cache;
}
首先是通过反射创建基础的容器,有了基础容器的Class类型,通过反射取得构造参数为String的构造方法,然后将id传入,完成基础容器的构建。
/**
* 通过反射获得基础缓存容器对象
* @param cacheClass 缓存类型
* @param id 标识符
* @return 基础缓存容器对象
*/
private Cache newBaseCacheInstance(Class extends Cache> cacheClass, String id) {
// 获取该类的构造方法
Constructor extends Cache> cacheConstructor = getBaseCacheConstructor(cacheClass);
try {
// 创建对象
return cacheConstructor.newInstance(id);
} catch (Exception e) {
throw new CacheException("Could not instantiate cache implementation (" + cacheClass + "). Cause: " + e, e);
}
}
/**
* 获取构造函数
* @param cacheClass 缓存类型
* @return 缓存类型的构造函数
*/
private Constructor extends Cache> getBaseCacheConstructor(Class extends Cache> cacheClass) {
try {
return cacheClass.getConstructor(String.class);
} catch (Exception e) {
throw new CacheException("Invalid base cache implementation (" + cacheClass + "). " +
"Base cache implementations must have a constructor that takes a String id as a parameter. Cause: " + e, e);
}
}
这个基础的容器该容器使用Map