Mybatis源码分析(3)---配置节点properties源码解析

1. 背景

在上篇Mybatis源码分析(2)—初始化配置文件内容中,我们通过追踪SqlSessionFactoryBuilder类的创建,了解到了Demo工程mybatisCodemybatis-config.xml配置文件在整个Mybatis中源码运行解析的过程,以及各节点解析后的信息注册到全局Configuration配置类中,然后以此创建了SqlSessionFactory对象,具体的流程我们通过以下图来展示:
Mybatis源码分析(3)---配置节点properties源码解析_第1张图片
今天开始,我们将对mybatis-config.xml中每个节点源码解析进行单独的介绍,按照源码中解析节点的顺序,我们本篇依然同Demo工程mybatisCode对节点properties进行深入的解析和测试。

2. 节点properties源码分析
  1. properties介绍:在mybatis官网中名为属性,具体的介绍如官网截图
    Mybatis源码分析(3)---配置节点properties源码解析_第2张图片
    通过官网的提供的例子和介绍,我们大概可以知道节点properties主要来指定一些属性的值,而值的来源可以通过其子节点property来配置,也可以通过外部properties后缀的文件来指定,接下来我们在来看Demo工程mybatisCode的mybatis-config.xml配置文件中,添加节点properties
    Mybatis源码分析(3)---配置节点properties源码解析_第3张图片
    在这里插入图片描述
    通过配置文件,我们知道节点properties给属性赋值的方式有三种:
  • resource方式:指定外部配置文件,通过外部配置文件进行动态的参数传递或 ${ }给各个子元素propertie的value赋值;
  • url 方式:指向外部配置文件的请求路径,通过外部配置文件进行动态的参数传递;
  • xml配置方式: 直接配置xml中子元素propertie中的value值;

那么它们之间赋值的顺序是怎么样的呢?
如果属性相同赋值的内容不同,最终以谁的赋值内容为准
带着上面两个问题,我们接下来开始对节点properties的源码解析进行分析。

  1. properties源码

解析properties节点的源码位置在于XMLConfigBuilder类中的propertiesElement(XNode context)方法

  /**
   * 解析节点的信息
   */
  private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
      //获取节点的所有子节点属性,放到属性容器defaults中
      Properties defaults = context.getChildrenAsProperties();
      //获取节点属性:resource
      String resource = context.getStringAttribute("resource");
      //获取节点属性:url
      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.");
      }
      //属性resource存在
      if (resource != null) {
        //加载resource属性值对应的properties文件中的键值对,并添加至defaults属性容器中
        defaults.putAll(Resources.getResourceAsProperties(resource));
      } else if (url != null) {//属性resource存在
        //通过url请求properties文件获取的键值对,并添加至defaults容器中
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
      //从全局Configuration配置类中获取属性容器vars
      Properties vars = configuration.getVariables();
      if (vars != null) {
        //属性容器vars中的键值对,并添加至属性容器defaults中
        defaults.putAll(vars);
      }
      //把属性容器defaults放到xml解析器中
      parser.setVariables(defaults);
      //最后把合并后的属性容器defaults重新注册到全局Configuration配置类中
      configuration.setVariables(defaults);
    }
  }

通过上面源码,我们可以得到很多信息:

  • 优先加载的子元素propertie的所有键值对,放到属性容器defaults中
  • resource和url的属性加载方式不能同时存在,也就是只能加载其他一个到外部配置文件到属性容器defaults中
  • 最后defaults整合全局Configuration配置类中的属性容器vars中重新注册回全局Configuration配置类中
  • 通过源码执行的顺序,我们可以大概确定属性加载赋值的顺序是:子元素propertie直接赋值 > resource > url

但是上面仅仅能确定是代码执行的顺序优先而已,对于相同属性的赋值最终以谁的赋值为准,我们接下来需要再来观察Properties类和其方法putAll(Properties properties)

public class Properties extends Hashtable<Object,Object> {
}
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>,Cloneable, java.io.Serializable {
   public synchronized void putAll(Map<? extends K, ? extends V> t) {
       for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
           put(e.getKey(), e.getValue());
   }
}
  • Properties类为Hashtable的子类,Hashtable是Map的实现类,也就是Properties类是Map的实现类
  • Properties的putAll方法,实际调的就是Hashtable的putAll(Map t)方法,我们知道Map中,存在相同key,新value会进行替换旧value

由此我们可以结合上面源码的知道了,存在相同key属性的情况下,子元素propertie直接赋值 会被resource 或url的赋值替换掉,最终的赋值已全局Configuration配置类中已存在的vars属性容器的赋值为准,通过源码分析和官网说明,对于上面提出的两个问题,我们已经心里有很明确的答案了
属性顺序加载的官网说明
最后我们通过图的形式,来形象的描述整个顺序加载的过程,帮助我们加深对源码解析的记忆:
Mybatis源码分析(3)---配置节点properties源码解析_第4张图片
Mybatis源码分析(3)---配置节点properties源码解析_第5张图片
到这里在补充一点,全局配置类中的已有的属性容器Properties vars从哪里来的呢?
我们回顾一下XMLConfigBuilder初始化的源码

  private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
  	//初始化父类BaseBuilder中的全局 Configuration 配置类
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    //给全局 Configuration 配置类注入属性容器 Properties props
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }

也就是来源创建SqlSessionFactory对象过程中,传入的方法Properties props参数,我们的Demo工程创建过程中,并没有传入,所以全局配置类中的已有的属性容器vars是null,这里就对应上面官网说明截图中的一句话:通过方法参数传递的属性具有最高优先级

3. 结尾

到此,节点properties在mybatis中的源码解析分析内容就这么多,下篇开始将对节点settings的源码解析过程进行分析

你可能感兴趣的:(Mybatis)