mybatis原始碼-解析配置檔案(四-1)之配置檔案Mapper解析(cache)
阿新 • • 發佈:2018-12-13
1. 簡介
本文章主要講解的是, xxxMapper.xml 檔案中, cache 節點的原始碼。
2. 解析
XMLMapperBuilder.cacheElement()
方法主要負責解析 <cache>
private void cacheElement(XNode context) throws Exception { if (context != null) { // 獲取 type 節點的屬性, 預設是 PERPETUAL String type = context.getStringAttribute("type", "PERPETUAL"); // 通過 type 值, 查詢對應 Cache 介面的實現 Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type); // eviction 屬性, eviction 對應的是回收策略, 預設為 LRU。 String eviction = context.getStringAttribute("eviction", "LRU"); // 解析 eviction 屬性指定的 Cache 裝飾器型別 Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction); // flushInterval 對應重新整理間隔, 單位毫秒, 預設值不設定, 即沒有重新整理間隔, 快取僅僅在重新整理語句時重新整理。 Long flushInterval = context.getLongAttribute("flushInterval"); // size 對應為引用的數量,即最多的快取物件資料。 Integer size = context.getIntAttribute("size"); // readOnly 為只讀屬性, 預設為 false, 即可讀寫 boolean readWrite = !context.getBooleanAttribute("readOnly", false); // blocking 為阻塞, 預設值為 false。 當指定為 true 時將採用 BlockingCache 進行封裝 boolean blocking = context.getBooleanAttribute("blocking", false); // 獲取 <cache> 屬性節點下的子節點, 用於初始化二級快取 Properties props = context.getChildrenAsProperties(); // 通過 MapperBuilderAssistant 建立 Cache 物件, 並將其新增到 COnfiguration 中 builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props); } }
其中, type 的對應型別 PERPETUAL
// PerpetualCache.class 為 org.apache.ibatis.cache.impl.PerpetualCache
typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
其他的就是獲取屬性, 有的有對應的預設值。
最後需要將這些屬性, 通過 MapperBuilderAssistant.useNewCache()
進行快取設定。
public Cache useNewCache(Class<? extends Cache> typeClass, Class<? extends Cache> evictionClass, Long flushInterval, Integer size, boolean readWrite, boolean blocking, Properties props) { // 建造者模式 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(); // 將物件新增到 configuration 中 configuration.addCache(cache); // 給當前名稱空間的快取成員變數賦值 currentCache = cache; return cache; }
該函式建立對應的 Cache 物件, 該物件的 id 為 currentNamespace(當前mapper.xml 的 namespace)。
public Cache build() { // 設定預設的實現, type 和 lru 對應的類不為空 setDefaultImplementations(); // 通過反射建立物件 Cache cache = newBaseCacheInstance(implementation, id); // 根據<cache>節點的子節點<property>, 初始化Cache物件 setCacheProperties(cache); // issue #352, do not apply decorators to custom caches // 如果是PerpetualCache型別, 使用 decorators 中的裝飾器來包裝cache, 並設定屬性 if (PerpetualCache.class.equals(cache.getClass())) { for (Class<? extends Cache> decorator : decorators) { cache = newCacheDecoratorInstance(decorator, cache); setCacheProperties(cache); } // mybatis 自己提供的標準裝飾器 cache = setStandardDecorators(cache); } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) { // 如果不是 LoggingCache 子類, 則新增 LoggingCache 裝飾器 cache = new LoggingCache(cache); } return cache; }
將物件新增到 configuratin 中。
public void addCache(Cache cache) {
caches.put(cache.getId(), cache);
}
對應的成員變數為
protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
是 StrictMap
型別。該物件將 namespace 與 快取物件 Cache 對應起來了。 而 namespace 是 xxxMapper.xml 的標識。
3 StrictMap
StrictMap
有什麼特殊的地方, 為什麼不直接用 HashMap
呢?
3.1 區別HashMap:鍵必須為String
protected static class StrictMap<V> extends HashMap<String, V>
3.2 區別HashMap:多了成員變數 name
多了一個 name 成員變數, 而且該變數是必須設定的
所有的建構函式都需要
public StrictMap(String name, int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
this.name = name;
}
public StrictMap(String name, int initialCapacity) {
super(initialCapacity);
this.name = name;
}
public StrictMap(String name) {
super();
this.name = name;
}
public StrictMap(String name, Map<String, ? extends V> m) {
super(m);
this.name = name;
}
3.3 區別HashMap:key 的處理多了一些變化
3.3.1 put
public V put(String key, V value) {
// 是否存在 key, 存在則直接報異常
if (containsKey(key)) {
throw new IllegalArgumentException(name + " already contains value for " + key);
}
// 獲取 shortKey
if (key.contains(".")) {
// 將 key 以 . 分割, 並獲取最後一項作為 shortKey
final String shortKey = getShortName(key);
if (super.get(shortKey) == null) {
// 如果 shorKey 對應在 Map 中沒有值, 則放入
super.put(shortKey, value);
} else {
// 如果 shorKey 對應在 Map 中有值, 則放入一個 Ambiguity 類
super.put(shortKey, (V) new Ambiguity(shortKey));
}
}
// key 也會放一個 value
return super.put(key, value);
}
3.3.2 shortKey
關於 shortKey, 其實就是我們以全限定名作為屬性時, 它取得是分隔符分割後最後的一項。
// 將 key 以 . 分割, 並獲取最後一項作為 shortKey
private String getShortName(String key) {
final String[] keyParts = key.split("\\.");
return keyParts[keyParts.length - 1];
}
shortKey 它的作用就是類似一個模糊查詢的功能, 比如說我們要呼叫的是 com.mybatis.homejim.mapper.StudentMapper.selectAll
這個函式, 我們可以寫
selectList("com.mybatis.homejim.mapper.StudentMapper.selectAll");
在 mybatis 中加入 shortKey 之後, 我們只需要寫
selectList("selectAll");
但是, 在實際使用時用處不大, 很多函式基本都是會是二義性的, 不明白為何不取消。
3.3.3 Ambiguity
Ambiguity
是 StrictMap
中的靜態內部類。
protected static class Ambiguity {
final private String subject;
public Ambiguity(String subject) {
this.subject = subject;
}
public String getSubject() {
return subject;
}
}
其作用記錄存在二義性的 key, 告訴使用者, 你的這個 key 是二義性的。
3.3.4 get
public V get(Object key) {
// value 為空則報錯
V value = super.get(key);
if (value == null) {
throw new IllegalArgumentException(name + " does not contain value for " + key);
}
// 二義性也報錯
if (value instanceof Ambiguity) {
throw new IllegalArgumentException(((Ambiguity) value).getSubject() + " is ambiguous in " + name
+ " (try using the full name including the namespace, or rename one of the entries)");
}
// 正常情況下應該是返回
return value;
}