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屬性。
原創不易,有不正之處歡迎指正。