2.3.解析 Properties 子元素 初始化属性配置

优质
小牛编辑
140浏览
2023-12-01

点击学习properties元素的配置方法

Mybatis用于解析XML的基础环境准备好之后,我们终于可以迎来第一次真正的配置文件的解析操作了。

propertiesElement(root.evalNode("properties"));

propertiesElement方法的作用是读取mybatis全局配置文件中的properties元素的配置,并将其转化为Properties对象。

properties元素是configuration元素中的11个子节点之一,他的DTD定义如下:

<!ELEMENT properties (property*)>
<!ATTLIST properties
resource CDATA #IMPLIED
url CDATA #IMPLIED
>

它允许有多个property子节点,同时也可以配置 resourceurl 属性,但是resourceurl属性不能同时存在, 其中property子节点的定义如下:

<!ELEMENT property EMPTY>
<!ATTLIST property
name CDATA #REQUIRED
value CDATA #REQUIRED
>

property元素是由namevalue构成的,因此其可以被解析成Properties对象。 在接下来具体的解析过程中,我们会看到Mybatis针对这三种不同的配置方式都给出了对应的解析操作。

/**
  * 解析Properties标签
  *
  * @param context Properties节点
  * @throws Exception 异常
  */
 private void propertiesElement(XNode context) throws Exception {
     // 用户配置了properties节点
     if (context != null) {
         // 获取Properties下得Property节点的name 和value值,并转换Properties属性
         Properties defaults = context.getChildrenAsProperties();
         // 获取引用的资源
         String resource = context.getStringAttribute("resource");
         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) {
             // 加载*.Properties资源配置文件
             defaults.putAll(Resources.getResourceAsProperties(resource));
         } else if (url != null) {
             // 加载*.Properties资源配置文件
             defaults.putAll(Resources.getUrlAsProperties(url));
         }
         // 获取用户编码传入的属性配置
         Properties vars = configuration.getVariables();
         if (vars != null) {
             // 合并属性配置
             defaults.putAll(vars);
         }
         // 刷新解析器中的变量引用,用于接下来的解析
         parser.setVariables(defaults);
         // 同步到Mybatis设置中
         configuration.setVariables(defaults);
     }
 }

首先,获取properties对象下所有proerty子节点,并据此生成一个Properties实例。

// 获取Properties下得Property节点的name 和value值,并转换Properties属性
Properties defaults = context.getChildrenAsProperties();

其中context.getChildrenAsProperties()代码如下:

/**
 * 获取指定包元素下的所有子节点,在读取其name和value属性后,转换为Properties对象。
 *
 */
public Properties getChildrenAsProperties() {
    Properties properties = new Properties();
    for (XNode child : getChildren()) {
        // 读取name和value属性
        String name = child.getStringAttribute("name");
        String value = child.getStringAttribute("value");
        if (name != null && value != null) {
            // 同时有name和value属性则保存
            properties.setProperty(name, value);
        }
    }
    return properties;
}

他的作用是获取当前节点下的所有元素节点,在读取其namevalue属性后,转换为Properties对象,其代码中getChildren()方法的作用是筛选出有效的元素节点集合:

public List<XNode> getChildren() {
     List<XNode> 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;
 }

在处理完propertiesproperty子节点之后,mybatis会分别获取propertieresourceurl两个属性。

mybatis先校验二者不得同时存在。

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属性的值加载对应的资源文件并将其内容转换成一个Properties对象保存到上一步获取到的Properties对象中。

if (resource != null) {
    // 加载*.Properties资源配置文件
    defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
    // 加载*.Properties资源配置文件
    defaults.putAll(Resources.getUrlAsProperties(url));
}

然后取出Configuration中的现有的变量(这些变量由用户编码传入),然后也保存到Properties对象中。

// 获取用户编码传入的属性配置
Properties vars = configuration.getVariables();
if (vars != null) {
   // 合并属性配置
   defaults.putAll(vars);
}

其实通过上诉的加载顺序我们不难理解Mybatis的属性配置,生效优先级由高到低依次为: 启动时设置的参数->Mybatis 主配置文件中propertiesurl/resource属性指向的配置 -> Mybatis 主配置文件中propertiesproperty配置。

到这一步,所有的全局属性配置就已经全部加载完毕了,接下来就是使用这些属性了。

首先更新xpathParser中的全局变量引用,方面接下来解析文件时的使用。

// 刷新解析器中的变量引用,用于接下来的解析
parser.setVariables(defaults);

然后把这些全局属性保存到Configuration中,properties元素的解析操作就完成了。

// 同步到Mybatis设置中
configuration.setVariables(defaults);

补充一下:

在上面我们在根据resource/url属性的值加载对应的资源文件并将其内容转换成一个Properties对象这一步操作中,涉及到了一个Resources对象,该对象是mybatis用来简化资源访问的一个工具类。

Resources对象中有一个ClassLoaderWrapper类型的属性,该属性是mybatis中定义的ClassLoader的包装类,他内部持有多个不同的ClassLoader实现,当用户尝试通过ClassLoaderWrapper获取资源时,改包装类会尝试使用多个ClassLoader来加载资源。

ResourcesClassLoaderWrapper的实现都相对比较简单,如果感兴趣的话,可以自己查看一下源码,这里就不再赘述了。