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屬性。

 

原創不易,有不正之處歡迎指正。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM