一、首先讀取類路徑下的配置文件,獲取其字節輸入流。
二、創建SqlSessionFactoryBuilder對象,調用內部的build方法。factory = new SqlSessionFactoryBuilder().build(in);
三、根據字節輸入流創建XMLConfigBuilder即解析器對象parser。XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//根據字節輸入流創建XMLConfigBuilder即解析器對象parser
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//返回的Configuration配置對象作爲build的參數
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
四、調用parser對象的parse方法,parser.parse()
,該結果將返回一個Configuration配置對象,作爲build方法的參數。
五、parse()方法中,調用parseConfiguration方法將Configuration元素下的所有配置信息封裝進Parser對象的成員Configuration對象之中。
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//將configuration的配置信息一一封裝到configuration中
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
六、其中進行解析xml元素的方式是將通過evalNode方法獲取對應名稱的節點信息。如:parseConfiguration(parser.evalNode("/configuration"));
,此時parser.evalNode("/configuration")
即爲Configuration下的所有信息。
七、parseConfiguration方法相當於將裏面每個元素的信息都單獨封裝到Configuration中。
值得一提的是,我們之後要分析基於代理模式產生dao的代理對象涉及到mappers的封裝,其實也在配置文件讀取封裝的時候就已經完成,也就是在parseConfiguration方法之中:mapperElement(root.evalNode("mappers"));
。他的作用就是,讀取我們主配置文件中<mappers>
的元素內容,也就是我們配置的映射配置文件。
<!-- 配置映射文件的位置 -->
<mappers>
<package name="com.smday.dao"></package>
</mappers>
private void mapperElement(XNode parent)
方法將mappers配置下的信息獲取,此處獲取我們resources包下的com.smday.dao包名。
接着就調用了configuration的addMappers方法,其實還是調用的是mapperRegistry。
public void addMappers(String packageName) {
mapperRegistry.addMappers(packageName);
}
讀到這裏,我們就會漸漸瞭解MapperRegistry這個類的職責所在,接着來看,這個類中進行的一些工作,在每次添加mappers的時候,會利用ResolverUtil類查找類路徑下的該包名路徑下,是否有滿足條件的類,如果有的話,就將Class對象添加進去,否則報錯。
緊接着,就到了一步比較重要的部分,當然只是我個人覺得,因爲第一遍看的時候,我沒有想到,這步居然可以封裝許許多多的重要信息,我們來看一看:
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
//如果已經綁定,則拋出異常
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
//將接口類作爲鍵,將MapperProxyFactory作爲值存入
knownMappers.put(type, new MapperProxyFactory<T>(type));
// 在運行解析器之前添加類型十分重要,否則可能會自動嘗試綁定映射器解析器
// 如果類型已知,則不會嘗試
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
//解析mapper映射文件,封裝信息
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
映射配置文件的讀取依靠namespace,我們可以通過查看源碼發現讀取映射配置文件的方法是loadXmlResouce(),所以namespace命名空間至關重要:
private void loadXmlResource() {
// Spring may not know the real resource name so we check a flag
// to prevent loading again a resource twice
// this flag is set at XMLMapperBuilder#bindMapperForNamespace
// 防止加載兩次,可以發現這句 判斷在許多加載資源文件的時候出現
if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
String xmlResource = type.getName().replace('.', '/') + ".xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
} catch (IOException e) {
// ignore, resource is not required
}
if (inputStream != null) {
XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
//最終解析
xmlParser.parse();
}
}
}
//xmlPaser.parse()
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
//讀取映射配置文件信息的主要代碼
configurationElement(parser.evalNode("/mapper"));
//加載完成將該路徑設置進去,防止再次加載
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
可以看到,對映射文件解析之後,mappedStatements對象中出現了以下內容:
至此,主配置文件和映射配置文件的配置信息就已經讀取完畢。
八、最後依據獲得的Configuration對象,創建一個new DefaultSqlSessionFactory(config)
。
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
總結:
-
解析配置文件的信息,並保存在Configuration對象中。
-
返回包含Configuration的DefaultSqlSession對象。