MyBatis之映射文件解析_二级缓存及cache与cache-ref的解析
二级缓存的开启
首先,在mybatis的配置文件中,要将settings的子标签setting的属性cacheEnabled的值设为true,默认即为true,即默认开启二级缓存。
其次在映射文件中,要定义cache标签或cache-ref,这说明这个命名空间开启二级缓存管理。mybatis中每个命名空间(每个映射文件)对应着一个Cache对象,并使用这个Cache对象进行二级缓存管理。如下图所示。
<mapper namespace="xxx.XxxMapper" >
<!-- 启动当前命名空间的二级缓存 -->
<cache></cache>
</mapper>
Cache接口及其实现类:
Cache是一个接口,用来管理二级缓存,mybatis提供了5个常用的实现类PerpetualCache(默认实现),FifoCache,LruCache,SoftCache,WeakCache。
public interface Cache {
String getId();
void putObject(Object key, Object value);
Object getObject(Object key);
Object removeObject(Object key);
void clear();
int getSize();
default ReadWriteLock getReadWriteLock() {
return null;
}
}
PerpetualCache就是用一个没有长度限制的Map存放缓存数据。
FifoCache默认限定可缓存1024个对象,如果超出这个长度,则按FIFO原则清空缓存中的数据
cache标签的配置
<cache type="Cache类型" eviction="FIFO" flushInterval="60000" size="512" readOnly="true" >
<property name="" value=""></property>
</cache>
其中type定义了一个类型,必须是一个实现了Cache接口的类,默认类型为PERPETUAL, 其他属性在解析cache都会处理,子标签property定义的name-value会作为Properties传给Cache对象
Congiuration的无参构造方法中,注册了上述实现类型的别名。也可自定义Cache的实现类,用来管理二级缓存。
public Configuration() {
// 注册了Cache的五个内部实现类的别名
typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
}
Configuration中二级缓存的数据结构
在Configuration中用Map对象存储命名空间对应的Cache对象。mybatis启动时会创建好这些对象。
使用两个Map对象表示二级缓存的配置:
Map<String, Cache> caches = new StrictMap<>("Caches collection"),其中键是命名空间字符串,值是这个命名空间所使用的Cache对象
Collection
映射文件中cache标签的解析
解析由XMLMapperBuilder中的方法cacheElement实现。它的主要功能就是获得cache各属性值,这里读取的属性有: type,eviction,flushInterval,size,readOnly,blocking,及子元素property中名-值对,这些属性都有默认值,如type的默认值是别名PERPETUAL表示的类型,eviction的默认值是别名LRU表示的值,所以只要有cache标签就会创建Cache对象,没有cache标签(context为null)则不会创建Cache,意味着当前namespace不使用二级缓存。
private void cacheElement(XNode context) {
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);
boolean blocking = context.getBooleanAttribute("blocking", false);
// 读取property子标签
Properties props = context.getChildrenAsProperties();
// 使用帮助类构建Cache对象
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
}
}
Cache对象的构建
MapperBuilderAssistant是一个帮助类,其useNewCache完成创建当前命名空间的二级缓存对象及设置,它首先创建CacheBuilder对象,将获得到属性值调用CacheBuilder中的对应方法,最后调用build方法构造出一个Cache对象,并将这个对象加到配置对象的属性caches中
Cache对象构造逻辑,CacheBuilder的build方法,负责构造Cache对象,其构造逻辑为:
默认的实现类是PerpetualCache,装饰类是LruCache
Cache实现类应该有一个带字符串的构造方法,使用这个构造方法创建Cache对象
使用反射调用Cache对象的set方法设置property子标签中设置的值,name的值即是Cache对象的属性名
如果Cache是PerpetualCache,则使用装饰者模式,先应用decorators中的Cache装饰对象,默认为LruCache,而PerpetualCache对象是其引用的代理,
Cache装饰类都有一个Cache的构造方法,如public LruCache(Cache delegate),使用这个方法创建Cache的装饰类对象作为Cache对象
再调用方法setStandardDecorators进行标准设置:
若Cache对象有size属性,设置这个属性值
若clearInterval不空,加ScheduledCache
若有readWrite,加SerializedCache
加LoggingCache
加SynchronizedCache
若blocking为true,加BlockingCache
如果Cache是不PerpetualCache,说明使用的是定制的Cache对象,则不使用上述的装饰者模式,
若Cache是LoggingCache,则加LoggingCache
从上可以看到,如cache标签不作任何设置,其返回的是:
SynchronizedCache对象,它的代理是LoggingCache对象
LoggingCache对象的代理是LruCache对象
LruCache对象的代理是PerpetualCache对象
//MapperBuilderAssistant中的方法
public Cache useNewCache(Class<? extends Cache> typeClass,
Class<? extends Cache> evictionClass,
Long flushInterval,
Integer size,
boolean readWrite,
boolean blocking,
Properties props) {
// 使用CacheBuilder构建Cache对象
// Cache的id是当前映射文件的命名空间
Cache cache = new CacheBuilder(currentNamespace)
.implementation(valueOrDefault(typeClass, PerpetualCache.class))
.addDecorator(valueOrDefault(evictionClass, LruCache.class))
.clearInterval(flushInterval)
.size(size)
.readWrite(readWrite)
.blocking(blocking)
.properties(props)
.build();
// 将当前创建的Cache对象保存到Configuration对象中
configuration.addCache(cache);
currentCache = cache;
return cache;
}
cache-ref标签及配置: 说明当前命名空间使用另一个命名空间Cache对象作为当前命名空间的Cache对象。它的配置如下:
在Configuration中用Map<String, String> cacheRefMap = new HashMap<>()表示,其中键是当前的命名空间字符串,值是另一个命名空间字符串,表示当前命名空间使用的用于管理二级缓存的Cache对象,就是后一个命名空间所使用的Cache对象
cache-ref标签解析的主方法为private void cacheRefElement(XNode context)运行时context即对应着标签cache-ref
以当前命名空间为键,cache-ref的属性namespace的值为值加到Configuration的集合cacheRefMap中,
然后创建CacheRefResolver对象
接着如果命名空间xxx中的Cache已存在,则当前命名空间的Cache就使用这个Cache对象,存在对应的MapperBuilderAssistant对象中,否则会抛出异常,并将CacheRefResolver对象加到Configuration对象的incompleteCacheRefs中,以便解析完所有的命名空间后再作处理,这个在方法incompleteCacheRefs完成,处理逻辑与上一样,都是使用MapperBuilderAssistant对象中的方法public Cache useCacheRef(String namespace),只是现在有异常,还找不到所引用的Cache对象,即不做任何处理了。
// cache-ref解析代码,CacheRefResolver对象存储了另一个命名空间字符串,通过这个字符串可以找到它的Cache对象
private void cacheRefElement(XNode context) {
if (context != null) {
configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));
CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace"));
try {
cacheRefResolver.resolveCacheRef();
} catch (IncompleteElementException e) {
configuration.addIncompleteCacheRef(cacheRefResolver);
}
}
}

浙公网安备 33010602011771号