首先要说下停更这么久的原因,因为工作上最近有点忙,加上学习债的繁重以至于没有继续更新Mybatis源码解析的文章,所以趁着忙里偷闲,来简单的更新一篇。
在上一篇文章深入浅出Mybatis源码解析——全局配置文件解析加载流程中,我们讲到了全局配置文件的一个加载的过程,既然只是一个加载的流程,那就有解析的流程的啦!因此大家也猜到了,本篇正是讲全局配置文件中的一些解析,在parseConfiguration方法中涉及了很多的标签解析,由于数量比较多,所以我只会抽出几个比较典型的来给大家说一下。
废话不多说了,我们继续吧!
在解析代码之前,首先贴一下parseConfiguration方法中的代码,也省的大家去翻上一篇的文章。
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
// 解析标签
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"));
// 把所获取到的setting属性放到Configuration对象中
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(root.evalNode("properties"))方法调用时,首先会根据properties这个标签的属性来获取properties的节点,然后才加以解析。那我们来看下propertiesElement方法中的具体实现:
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
// 获取子标签标签的内容
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.");
}
// 如果resource或者url的值不为空,则加载对应的资源信息,然后合并到defaults中
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
parser.setVariables(defaults);
configuration.setVariables(defaults);
}
}
上面的代码中,首先会判断XNode是否为null,如果不为null才会进行解析,从代码逻辑中得知,有如下几个流程:
看上去代码逻辑还是非常简单的,那就再看下这几个流程的代码具体实现,首先看第一步:
public List getChildren() {
List children = new ArrayList<>();
NodeList nodeList = node.getChildNodes();
if (nodeList != null) {
for (int i = 0, n = nodeList.getLength(); i < n; i++) {
Node node = nodeList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
children.add(new XNode(xpathParser, node, variables));
}
}
}
return children;
}
public Properties getChildrenAsProperties() {
// 新建Properties对象
Properties properties = new Properties();
// 遍历字标签,并获取其name和value
for (XNode child : getChildren()) {
String name = child.getStringAttribute("name");
String value = child.getStringAttribute("value");
if (name != null && value != null) {
properties.setProperty(name, value);
}
}
return properties;
}
代码实现一如既往的比较简单,只是把所获取到的子标签进行遍历,并获取其name和value的值,如果不为空就放到Properties对象中,最后返回Properties对象。其实到这里properties标签的解析就已经基本完成了。
private Properties settingsAsProperties(XNode context) {
if (context == null) {
return new Properties();
}
Properties props = context.getChildrenAsProperties();
// Check that all settings are known to the configuration class
// 通过映射获取 本地映射工厂中的数据
MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
for (Object key : props.keySet()) {
if (!metaConfig.hasSetter(String.valueOf(key))) {
throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
}
}
return props;
}
public Properties getChildrenAsProperties() {
// 新建Properties对象
Properties properties = new Properties();
// 遍历字标签,并获取其name和value
for (XNode child : getChildren()) {
String name = child.getStringAttribute("name");
String value = child.getStringAttribute("value");
if (name != null && value != null) {
properties.setProperty(name, value);
}
}
return properties;
}
从代码逻辑上来讲,settings标签和properties标签并没有太多的差别。只是多了一步通过映射获取 本地映射工厂中的数据,然后来把这两个获取到的数据进行遍历比较,如果metaConfig来检查Properties对象中的key,则不符合则抛出异常。
这里只是选了两个,两个简单的标签解析进行说明,其实这篇文章只是为了要承接以下上篇文章中的一些不足,因此补上作为附篇文章,至于parseConfiguration方法中的mapperElement(root.evalNode("mappers"))方法,将会单独作为一篇文章来写。