zoukankan      html  css  js  c++  java
  • mybatis源码配置文件解析之一:解析properties标签

    mybatis作为日常开发的常用ORM框架,在开发中起着很重要的作用,了解其源码对日常的开发有很大的帮助。源码版本为:3-3.4.x,可自行到github进行下载。

    从这篇文章开始逐一分析mybatis的核心配置文件(mybatis-config.xml),今天先来看properties标签的解析过程。

    一、概述

    在单独使用mybatis的时候,mybatis的核心配置文件(mybatis-config.xml)就显的特别重要,是整个mybatis运行的基础,只有把配置文件中的各个标签正确解析后才可以正确使用mybatis,下面看properties标签的配置,properties标签的作用就是加载properties文件或者property标签,下面看其具体配置,实例如下

    <properties resource="org/mybatis/example/config.properties">
      <property name="username" value="dev_user"/>
      <property name="password" value="F2Fa3!33TYyg"/>
    </properties>

    上面是配置的properties标签的配置,在标签中配置了resource属性和property子标签。下面看具体的解析流程,这里分析properties标签的解析过程,启动流程暂不说,直接看解析的代码。

    二、详述

    上面,看到了properties标签的配置,下面看其解析方法,这里只粘贴部分代码,下面是parseConfiguration方法的代码,

    private void parseConfiguration(XNode root) {
        try {
          //issue #117 read properties first
          //解析properties标签    
          propertiesElement(root.evalNode("properties"));
          //解析settings标签
          Properties settings = settingsAsProperties(root.evalNode("settings"));
          loadCustomVfs(settings);
          //解析别名标签,例<typeAlias alias="user" type="cn.com.bean.User"/>
          typeAliasesElement(root.evalNode("typeAliases"));
          //解析插件标签
          pluginElement(root.evalNode("plugins"));
          //解析objectFactory标签,此标签的作用是mybatis每次创建结果对象的新实例时都会使用ObjectFactory,如果不设置
          //则默认使用DefaultObjectFactory来创建,设置之后使用设置的
          objectFactoryElement(root.evalNode("objectFactory"));
          //解析objectWrapperFactory标签
          objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
          //解析reflectorFactory标签
          reflectorFactoryElement(root.evalNode("reflectorFactory"));
          settingsElement(settings);
          // read it after objectFactory and objectWrapperFactory issue #631
          //解析environments标签
          environmentsElement(root.evalNode("environments"));
          databaseIdProviderElement(root.evalNode("databaseIdProvider"));
          typeHandlerElement(root.evalNode("typeHandlers"));
          //解析<mappers>标签
          mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
          throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
      }

    从上面的代码中可以找到下面的代码,即为解析的代码,

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

    这个方法就是解析properties标签,下面看具体的解析过程。

    1、解析子标签和属性

    /**
     * 解析mybatis-config.xml文件中的properties标签
     *<properties resource="org/mybatis/example/config.properties">
     *<property name="username" value="dev_user"/>
     *<property name="password" value="F2Fa3!33TYyg"/>
     *</properties>
     *解析步骤:
     *1、解析配置的property标签,放到defaults中;
     *2、解析resource或url属性,放到defaults中;
     *3、获取configuration中的variables变量值,放到defaults中
     * @param context
     * @throws Exception
     */
      private void propertiesElement(XNode context) throws Exception {
        if (context != null) {
          //1、读取properties标签中的property标签<property name="" value=""/>
          Properties defaults = context.getChildrenAsProperties();
          //2、读取properties标签中的resource、url属性
          String resource = context.getStringAttribute("resource");
          String url = context.getStringAttribute("url");
          //resource和url属性不能同时出现在properties标签中
          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不为空,则解析为properties,放到defaults中,由于defaults是key-value结构,所以会覆盖相同key的值
          if (resource != null) {
            defaults.putAll(Resources.getResourceAsProperties(resource));
          } else if (url != null) {//如果url不为空,则解析为properties,放到defaults中,由于defaults是key-value结构,所以会覆盖相同key的值
            defaults.putAll(Resources.getUrlAsProperties(url));
          }
          //3、获得configuration中的variables变量的值,此变量可以通过SqlSessionFactoryBuilder.build()传入properties属性值
          Properties vars = configuration.getVariables();
          //如果调用build的时候传入了properties属性,放到defaults中
          if (vars != null) {
            defaults.putAll(vars);
          }
          //放到parser和configuration对象中
          parser.setVariables(defaults);
          configuration.setVariables(defaults);
        }
      }

    从上面的解析过程可以看到,首先解析properties标签的子标签,也就是property标签,通过下面的方法获得,

    //1、读取properties标签中的property标签<property name="" value=""/>
          Properties defaults = context.getChildrenAsProperties();

    解析property标签,并放到Properties对象中。那么是如何放到Properties对象中的那,在getChildrenAsProperties方法中,

    public Properties getChildrenAsProperties() {
        Properties properties = new Properties();
        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;
      }

    可以看出是循环property标签,获得其name和value属性,并放入properties对象中。

    接着解析properties的resource和url属性,如下

    //2、读取properties标签中的resource、url属性
          String resource = context.getStringAttribute("resource");
          String url = context.getStringAttribute("url");

    分别获得resource和url属性,这里这两个属性都是一个路径。

    2、处理属性

    下面看这个判断,

    //resource和url属性不能同时出现在properties标签中
          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.");
          }

    这个判断表明在properties标签中,resource和url属性不能同时出现。

    2.1、处理resource和url属性

    下面看resource和url属性的处理,这里resource和url两个属性都是代表的一个路径,所以这里肯定是需要读取相应路径下的文件。

    //如果resource不为空,则解析为properties,放到defaults中,由于defaults是key-value结构,所以会覆盖相同key的值
          if (resource != null) {
            defaults.putAll(Resources.getResourceAsProperties(resource));
          } else if (url != null) {//如果url不为空,则解析为properties,放到defaults中,由于defaults是key-value结构,所以会覆盖相同key的值
            defaults.putAll(Resources.getUrlAsProperties(url));
          }

    下面看对resource的处理,调用的Resources.getResourceAsProperties(resource))方法,对resource进行处理,

    public static Properties getResourceAsProperties(String resource) throws IOException {
        Properties props = new Properties();
        InputStream in = getResourceAsStream(resource);
        props.load(in);
        in.close();
        return props;
      }

    从上面的代码可以看出是要转化为InputStream,最后放到Properties对象中,这里加载文件的详细过程,后面再详细分析。

    下面看对url的处理,调用Resources.getUrlAsProperties(url)方法,对url进行处理,

    public static Properties getUrlAsProperties(String urlString) throws IOException {
        Properties props = new Properties();
        InputStream in = getUrlAsStream(urlString);
        props.load(in);
        in.close();
        return props;
      }

    上面的代码依然是把url代表的文件处理成Properties对象。

    2.3、处理已添加的Properties

    在上面处理完property子标签、resource和url属性后,还进行了下面的处理,即从configuration中获得properties,

    //3、获得configuration中的variables变量的值,此变量可以通过SqlSessionFactoryBuilder.build()传入properties属性值
          Properties vars = configuration.getVariables();
          //如果调用build的时候传入了properties属性,放到defaults中
          if (vars != null) {
            defaults.putAll(vars);
          }

    如果configuration中已经存在properties信息,则取出来,放到defaults中。

    2.4、放入configuration对象中

    经过上面的处理,最后把所有的properties信息放到configuration中,

    //放到parser和configuration对象中
          parser.setVariables(defaults);
          configuration.setVariables(defaults);

    把defaults放到了configuration的variables属性中,代表的是整个mybatis环境中所有的properties信息。这个信息可以在mybatis的配置文件中使用${key}使用,比如,${username},则会从configuration的variables中寻找key为username的属性值,并完成自动属性值替换。

    三、总结

    上面分析了properties标签的解析过程,先解析property标签,然后是resource、url属性,最后是生成SqlSessionFactory的使用调用SqlSessionFactoryBuilder的build方法,传入的properties,从上面的解析过程,可以知道如果存在重复的键,那么最先解析的会被后面解析的覆盖掉,也就是解析过程是:property子标签-->resource-->url-->开发者设置的,那么覆盖过程为:开发者设置的-->url-->resource-->property子标签,优先级最高的为开发者自己设置的properties属性。

    原创不易,有不正之处欢迎指正。

  • 相关阅读:
    BZOJ 1101 莫比乌斯函数+分块
    BZOJ 2045 容斥原理
    BZOJ 4636 (动态开节点)线段树
    BZOJ 2005 容斥原理
    BZOJ 2190 欧拉函数
    BZOJ 2818 欧拉函数
    BZOJ 3123 主席树 启发式合并
    812. Largest Triangle Area
    805. Split Array With Same Average
    794. Valid Tic-Tac-Toe State
  • 原文地址:https://www.cnblogs.com/teach/p/12693588.html
Copyright © 2011-2022 走看看