mybatis原始碼學習------cache-ref和cache的解析
XMLMapperBuilder
介紹
XMLMapperBuilder類繼承自BaseBuilder類,該類也使用了建造者設計模式。XMLMapperBuilder類的主要作用是解析xxxMapper.xml配置檔案中使用者的配置,如下圖所示。
mybatis-3-mapper.dtd檔案中對於mapper節點的定義如下:
XMLMapperBuilder類的核心功能就是解析<mapper></mapper>
標籤中配置的內容。
欄位
XMLMapperBuilder類中定義的欄位如下
//xpath解析器,用於解析用於配置的xml
private final XPathParser parser;
//Mapper建造者助手,一個小工具
private final MapperBuilderAssistant builderAssistant;
//sql片段集合,key為sql片段的namespace+id,value為對應的XNode節點
private final Map<String, XNode> sqlFragments;
//xml配置的資源路徑
private final String resource;
建構函式
除了兩個已經過時的方法外,所有的建構函式都通過過載呼叫了最後一個建構函式
@Deprecated
public XMLMapperBuilder(Reader reader, Configuration configuration, String resource, Map<String, XNode> sqlFragments, String namespace) {
this(reader, configuration, resource, sqlFragments);
this.builderAssistant.setCurrentNamespace(namespace);
}
@Deprecated
public XMLMapperBuilder(Reader reader, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
this(new XPathParser(reader, true, configuration.getVariables(), new XMLMapperEntityResolver()),
configuration, resource, sqlFragments);
}
public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments, String namespace) {
this(inputStream, configuration, resource, sqlFragments);
//給builderAssistant設定名稱空間
this.builderAssistant.setCurrentNamespace(namespace);
}
public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
configuration, resource, sqlFragments);
}
//儲存全域性的配置,並建立一個BuilderAssistant例項,便於後續使用
private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
super(configuration);
//構造一個builderAssistant例項,方便後面解析
this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
this.parser = parser;
this.sqlFragments = sqlFragments;
this.resource = resource;
}
核心方法
XMLMapperBuilder類的核心方法為parse()方法,通過呼叫該方法,觸發對mapper.xml檔案中內容的解析。
parse()方法的主要邏輯是解析mapper節點中的配置,並做一些後續的收尾工作。parse()方法的定義如下:
/**
* XMLMapperBuilder類的核心方法,是解析mapper.xml檔案配置的入口
*/
public void parse() {
//防止重複載入
if (!configuration.isResourceLoaded(resource)) {
//解析mapper配置
configurationElement(parser.evalNode("/mapper"));
//將讀取過的配置檔案的地址儲存在configuration物件的loadedResources欄位中,
//該欄位用於防止資源被重複載入
configuration.addLoadedResource(resource);
//為讀取的mapper配置資訊繫結名稱空間
bindMapperForNamespace();
}
//解析待定的<resultMap>節點
parsePendingResultMaps();
//解析待定的<cache-ref>節點
parsePendingCacheRefs();
//解析待定的sql片段節點
parsePendingStatements();
}
解析mapper配置
該方法的作用是根據dtd檔案中的定義,逐一解析<mapper></mapper>
下的所有配置,方法實現如下:
private void configurationElement(XNode context) {
try {
//獲取配置檔案中配置的名稱空間
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.isEmpty()) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
//設定BuilderAssistant例項當前的名稱空間為mapper.xml檔案中配置的名稱空間
builderAssistant.setCurrentNamespace(namespace);
//解析<cache-ref/>配置
cacheRefElement(context.evalNode("cache-ref"));
//解析<cache/>配置
cacheElement(context.evalNode("cache"));
//解析<parameterMap/>配置
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
//解析<resultMap/>配置
resultMapElements(context.evalNodes("/mapper/resultMap"));
//解析<sql/>配置
sqlElement(context.evalNodes("/mapper/sql"));
//解析<select/>、<insert/>、<update/>和<delete/>配置
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
解析快取引用配置
對於< cache-ref />的作用,可參考官網文件的介紹:
對某一名稱空間的語句,只會使用該名稱空間的快取進行快取或重新整理。如果想要在多個名稱空間中共享相同的快取配置和例項,則可以使用 cache-ref 元素來引用另一個快取
cacheRefElement方法的主要邏輯就是構建一個快取引用解析器物件CacheRefResolver,並通過CacheRefResolver物件來解析使用者配置的快取
private void cacheRefElement(XNode context) {
if (context != null) {
//在全域性配置物件的cacheRefMap欄位中維護快取引用的關係,
//cacheRefMap中的對映關係為,當前名稱空間--->配置中宣告的名稱空間
configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));
//構造CacheRefResolver物件
CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace"));
try {
//解析快取引用,實際的解析工作是委派給builderAssistant物件來完成
cacheRefResolver.resolveCacheRef();
} catch (IncompleteElementException e) {
//如果在解析的過程中丟擲了IncompleteElementException異常,則將當前的配置解析器物件新增到configuration物件的待完成快取引用的列表中
configuration.addIncompleteCacheRef(cacheRefResolver);
}
}
}
CacheRefResolver類
根據resolveCacheRef()方法的定義可以發現,mybatis對於快取引用的解析是委派給了MapperBuilderAssistant類的例項去完成的。
public class CacheRefResolver {
//構造小助手
private final MapperBuilderAssistant assistant;
//引用的名稱空間
private final String cacheRefNamespace;
public CacheRefResolver(MapperBuilderAssistant assistant, String cacheRefNamespace) {
this.assistant = assistant;
this.cacheRefNamespace = cacheRefNamespace;
}
public Cache resolveCacheRef() {
return assistant.useCacheRef(cacheRefNamespace);
}
}
MapperBuilderAssistant#useCacheRef方法的定義如下:
/**
*
* @param namespace 配置檔案中引用的名稱空間
* @return
*/
public Cache useCacheRef(String namespace) {
if (namespace == null) {
throw new BuilderException("cache-ref element requires a namespace attribute.");
}
try {
unresolvedCacheRef = true;
//在全域性配置物件中查詢該 namespace 對應的快取物件
Cache cache = configuration.getCache(namespace);
//如果在configuration物件中沒有找到對應的快取例項,則丟擲異常
//並將當前的解析任務放進incompleteCacheRefs集合中,後續會呼叫
//XmlMapperBuilder#parsePendingCacheRefs()方法對其進行再次解析
if (cache == null) {
throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.");
}
currentCache = cache;
unresolvedCacheRef = false;
return cache;
} catch (IllegalArgumentException e) {
throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.", e);
}
}
解析快取配置
二級快取預設配置時的行為
mybatis二級快取預設配置的作用如下,他的作用域是全域性的:
- 對映語句檔案中的所有 select 語句的結果將會被快取。
- 對映語句檔案中的所有 insert、update 和 delete 語句會重新整理快取。
- 快取會使用最近最少使用演算法(LRU, Least Recently Used)演算法來清除不需要的快取。
- 快取不會定時進行重新整理(也就是說,沒有重新整理間隔)。
- 快取會儲存列表或物件(無論查詢方法返回哪種)的 1024 個引用。
- 快取會被視為讀/寫快取,這意味著獲取到的物件並不是共享的,可以安全地被呼叫者修改,而不干擾其他呼叫者或執行緒所做的潛在修改。
cacheElement的程式碼如下,其中:
- type配置的是快取的型別,預設為PERPETUAL,也就是使用HashMap作為快取的容器。
- eviction配置的是快取的清除策略,可用的清除策略有LRU、FIFO、SOFT和WEAK,其中LRU為預設策略。
- flushInterval獲取快取的清除間隔,可以配置為任意正整數,單位為毫秒,預設為0。
- size配置的是最大快取的引用個數,可以是任意正整數,預設為快取1024個引用。
- readOnly屬性可以被設定為 true 或 false。只讀的快取會給所有呼叫者返回快取物件的相同例項。 因此這些物件不能被修改。這就提供了可觀的效能提升。而可讀寫的快取會(通過序列化)返回快取物件的拷貝。 速度上會慢一些,但是更安全,因此預設值是 false。
- blocking表示當在快取中獲取不到資料時,是否會阻塞後續的請求,預設為false。
//解析<cache/>配置,最後委派給MapperBuilderAssistant的例項去完成快取物件的建立任務
private void cacheElement(XNode context) {
if (context != null) {
//獲取type屬性的配置,如果未配置,則預設值為PERPETUAL
String type = context.getStringAttribute("type", "PERPETUAL");
Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
//獲取eviction屬性的配置,如果未配置,則預設值為LRU
String eviction = context.getStringAttribute("eviction", "LRU");
Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
//獲取eviction屬性的配置
Long flushInterval = context.getLongAttribute("flushInterval");
//獲取size屬性的配置
Integer size = context.getIntAttribute("size");
//獲取readOnly屬性的配置
boolean readWrite = !context.getBooleanAttribute("readOnly", false);
//獲取blocking屬性的配置
boolean blocking = context.getBooleanAttribute("blocking", false);
//獲取<cache/>標籤中配置的property屬性
Properties props = context.getChildrenAsProperties();
//根據使用者配置建立一個快取物件,並將其新增到configuration的caches屬性中
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
}
}
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;
}
相關文章
- mybatis原始碼學習------resultMap和sql片段的解析MyBatis原始碼SQL
- Mybatis 原始碼學習(二)MyBatis原始碼
- MyBatis原始碼解析MyBatis原始碼
- mybatis plugin原始碼解析MyBatisPlugin原始碼
- Mybatis原始碼分析(二)XML的解析和Annotation的支援MyBatis原始碼XML
- Golang 學習——error 和建立 error 原始碼解析GolangError原始碼
- 通過原始碼學習@functools.lru_cache原始碼
- 深入Mybatis原始碼——配置解析MyBatis原始碼
- Mybatis原始碼解析4——SqlSessionMyBatis原始碼SQLSession
- Spring原始碼深度解析(郝佳)-學習-原始碼解析-Spring MVCSpring原始碼MVC
- mybatis通用mapper原始碼解析(一)MyBatisAPP原始碼
- mybatis通用mapper原始碼解析(二)MyBatisAPP原始碼
- myBatis原始碼解析-反射篇(4)MyBatis原始碼反射
- MyBatis詳細原始碼解析(上篇)MyBatis原始碼
- mybatis原始碼解析(四)--- MapperStatement的註冊MyBatis原始碼APP
- 【Mybatis原始碼解析】- JDBC連線資料庫的原理和操作MyBatis原始碼JDBC資料庫
- mybatis原始碼學習:從SqlSessionFactory到代理物件的生成MyBatis原始碼SQLSession物件
- mybatis原始碼學習:一級快取和二級快取分析MyBatis原始碼快取
- myBatis原始碼解析-快取篇(2)MyBatis原始碼快取
- myBatis原始碼解析-日誌篇(1)MyBatis原始碼
- Mybatis原始碼解析2—— 例項搭建MyBatis原始碼
- 不學無數——Mybatis解析判斷表示式原始碼分析MyBatis原始碼
- MyBatis原始碼學習筆記(一) 初遇篇MyBatis原始碼筆記
- MyBatis原始碼窺探(一):MyBatis整體架構解析MyBatis原始碼架構
- mybatis原始碼解析-日誌介面卡MyBatis原始碼
- 從原始碼的角度解析Mybatis的會話機制原始碼MyBatis會話
- Spring原始碼深度解析(郝佳)-學習-原始碼解析-基於註解注入(二)Spring原始碼
- Java併發包原始碼學習系列:JDK1.8的ConcurrentHashMap原始碼解析Java原始碼JDKHashMap
- Java併發包原始碼學習系列:同步元件CountDownLatch原始碼解析Java原始碼元件CountDownLatch
- Java併發包原始碼學習系列:同步元件CyclicBarrier原始碼解析Java原始碼元件
- Java併發包原始碼學習系列:同步元件Semaphore原始碼解析Java原始碼元件
- myBatis原始碼解析-二級快取的實現方式MyBatis原始碼快取
- mybatis plus原始碼解析(一) ---基於springboot配置載入和SqlSessionFactory的構造MyBatis原始碼Spring BootSQLSession
- myBatis原始碼解析-資料來源篇(3)MyBatis原始碼
- myBatis原始碼解析-型別轉換篇(5)MyBatis原始碼型別
- (一) Mybatis原始碼分析-解析器模組MyBatis原始碼
- Mybatis原始碼解析之執行SQL語句MyBatis原始碼SQL
- 【Mybatis原始碼解析】- 整體架構及原理MyBatis原始碼架構