前言
上一篇我們通過一個簡單的例子了解了基于XML配置文件的方式初始化Mybatis的過程,毫不夸張的說,MyBatis初始化的過程,就是創建 Configuration對象的過程。而在構建SqlSessionFactory的過程中,生成了XMLConfigBuilder實例對象,所以我們就先從XMLConfigBuilder說起。
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
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.
}
}
}
XMLConfigBuilder
首先我們先看下XMLConfigBuilder實例的創建過程,如上述所示,入參為inputStream,environment和properties, 如下所示,是通過生成XPathParser實例后調用私有構造函數來生成的。XPathParser封裝了JDK中的Document和XPath對象,其實就是一個工具類,主要用來解析XML文件內容的。
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
// 私有構造函數,對應上面代碼中的this
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
其實分析到這,Mybatis的初始化(其實就是創建Configuration實例的過程)過程就比較清晰了:
- 首先創建XMLConfigBuilder 實例,然后創建了 Configuration 實例,順便把解析XML文件內容的工具類也創建好
- 在 XMLConfigBuilder 當中解析 Configuration的每一個節點,并設置到Configuration當中去
- 最后返回 Configuration 實例,并以其為入殘參,來生成SqlSessionFactory實例
// 解析 Configuration的每一個節點
private void parseConfiguration(XNode root) {
try {
Properties settings = settingsAsPropertiess(root.evalNode("settings"));
propertiesElement(root.evalNode("properties"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectionFactoryElement(root.evalNode("reflectionFactory"));
settingsElement(settings);
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
BaseBuilder
XMLConfigBuilder繼承了 BaseBuilder,事實上,其他所有解析XML的Builder都繼承了這個類。下面是BaseBuilder的大致結構,由此看出,Configuration實例其實是存放在BaseBuilder中的。
public abstract class BaseBuilder {
protected final Configuration configuration;
protected final TypeAliasRegistry typeAliasRegistry;
protected final TypeHandlerRegistry typeHandlerRegistry;
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}
...... // 此處省略了很多方法
}
下圖是BaseBuilder的實現依賴圖,你會發現,基本上所有解析XML的類都需要即成這個基類,其中的幾個類(Mapper相關的)以后會詳細描述。
XMLConfigBuilder 的具體工作
上問我們了解到,繼承了 BaseBuilder 的 XMLConfigBuilder, 其實是Myabtis初始化的真正入口,那 XMLConfigBuilder 實際上都做了些什么工作呢?其實都在 XMLConfigBuilder 實例的parse方法內。
// parse方法
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
從代碼上可以看出,其實parse()方法也是拋磚引玉的方法,真正干活的其實是 parseConfiguration 方法
private void parseConfiguration(XNode root) {
try {
Properties settings = settingsAsPropertiess(root.evalNode("settings"));
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectionFactoryElement(root.evalNode("reflectionFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
Mybatis 配置文件之解析詳解
從上面的 parseConfiguration 方法可以看出,Mybatis 配置文件中支持的節點類型包含如下內容,先寫到這里,下篇文章依次來了解下每一項的解析過程.
- properties 屬性
- settings 設置
- typeAliases 類型命名
- plugins 插件
- objectFactory 對象工廠
- objectWrapperFactory
- reflectionFactory
- environments 環境
- databaseIdProvider 數據庫廠商標識
- typeHandlers 類型處理器
- mappers 映射器