构造Configuration对象
文章一中说到
最后XmlConfigBulider调用了parseConfiguration方法,传递了一个Xnode对象。
Xnode对象类似于一棵树。
通过getStringAttribute方法去获取根节点的标签对应的属性。
通过evalNode方法传递每个子标签名去获取子节点Xnode。
private void parseConfiguration(XNode root) {
try {
//XNode从properties标签开始解析,生成一个子Xnode,传递给propertiesElement方法
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(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);
}
}
propertiesElement方法(举例说明)
首先看properties标签,它里面有个属性是resource,resource值是jdbc连接参数文件的位置
然后进入propertiesElement方法查看,因为properties是个单独的标签,没有子标签,所以对应的Xnode也没有子节点
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
//defaults == null
Properties defaults = context.getChildrenAsProperties();
//jdbc.properties
String resource = context.getStringAttribute("resource");
//url == null
String url = context.getStringAttribute("url");
//如果resource属性和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取得Porerties,就是把jdbc.properties文件里面的内容,
//封装成了一个hashtale结构
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
//将连接参数properties传给Xpathparser的Variables属性
parser.setVariables(defaults);
//将连接参数properties传给configuration的Variables属性
configuration.setVariables(defaults);
}
}
此时configuration的第一个属性variables就构造好了,
后面的方法目的也是为了构造configuration的其他属性,
最终结果就是,先将mybatis-xml配置文件转为Document对象再转为Configuration对象。
mapperElement方法(重点)
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//child.getName() == mapper
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
//resource == "mapper/UserMapper.xml"
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
try(InputStream inputStream = Resources.getResourceAsStream(resource)) {
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
}
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
try(InputStream inputStream = Resources.getUrlAsStream(url)){
//读取mapper/UserMapper.xml的文件流,创建XMLMapperBuilder对象
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
//构造
mapperParser.parse();
}
} else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}