总览
上图为mybatis配置文件与实体类的对应关系,先有个总体印象
配置文件解析入口
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
- 进入build方法,看一下是如何实现的
// Class: SqlSessionFactoryBuilder
public SqlSessionFactory build(InputStream inputStream, String environment) {
return build(inputStream, environment, null);
}
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,这个类负责解析我们的配置文件。
-
进入parses方法
// Class: XMLConfigBuilder public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; // 解析Configuration标签 parseConfiguration(parser.evalNode("/configuration")); return configuration; }
-
进入parseConfiguration,看一下具体实现
// Class: XMLConfigBuilder
private void parseConfiguration(XNode root) {
try {
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
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);
}
}
这里对Configuration里的字标签进行了挨个解析。
1. 解析properties标签
- 先来看一下properties标签的配置内容
<properties resource="db.properties">
<property name="jdbc.username" value="coolblog"/>
<property name="hello" value="world"/>
</properties>
可以看到既可以读取资源文件,还可以在子标签中自定义属性。
- 看一下源码的解析过程
// Class: XMLConfigBuilder
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
// 获取子节点的属性并作为Properties
Properties defaults = context.getChildrenAsProperties值
// 获取属性resource的值
String resource = context.getStringAttribute("resource");
// 获取属性url的值
String url = context.getStringAttribute("url");
if (resource != null && url != null) {
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
if (resource != null) {
// resource不为空,将解析到的属性添加到Properties中
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
// url不为空,将解析到的属性添加到Properties中
defaults.putAll(Resources.getUrlAsProperties(url));
}
// 从configuration中获取属性,也添加到Properties中
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
// 将属性放入到解析器中
parser.setVariables(defaults);
// 将属性放入到configuration中
configuration.setVariables(defaults);
}
}
2. 解析plugins标签
- 先看一下plugin标签的配置内容
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="key" value="value"/>
</plugin>
</plugins>
- 看一下源码的解析过程
// Class: XMLConfigBuilder
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
// 循环处理plugins标签的子标签
for (XNode child : parent.getChildren()) {
// 获取interceptor属性的值
String interceptor = child.getStringAttribute("interceptor");
// 获取配置信息
Properties properties = child.getChildrenAsProperties();
// 实例化拦截器
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
// 设置拦截器的属性
interceptorInstance.setProperties(properties);
// 将拦截器添加到configuration中
configuration.addInterceptor(interceptorInstance);
}
}
}
3. 解析environments标签
- 先看一下environments标签的配置内容
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
- 看一下源码的解析过程
// Class: XMLConfigBuilder
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
// 获取default属性中的值
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
// 如果default的值与id值相同
if (isSpecifiedEnvironment(id)) {
// 通过事务管理气的配置创建TransactionFactory
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
// 根据配置创建数据源对象
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
// 将数据源和事务管理器对象添加到Environment中
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
// 将Environment添加到configuration中
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
.transactionFactory(txFactory)
.dataSource(dataSource);
// 将Environment添加到configuration中
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}