MyBatis源碼分析:MyBatis是如何加載mybatis-config.xml文件的?

一、MyBatis框架圖及瞎BB

先看圖:

                                                (圖片來源:《MyBatis技術內幕》) 

Mybatis加載mybatis-config.xml的過程其實就是 MyBatis的基礎支持層的解析器模塊的工作流程,此篇博客以加載一個xml文件爲例來讓你對解析器的有個全面的瞭解。本人是一步一步跟着源碼調試走下去的,不敢說自己很瞭解,只是把自己的試驗結果分享出來,當做筆記同時也能幫助別人。(媽的,說話間頭髮又掉了幾根在桌子上,程序挺好搞的,就是頭有點涼。)

二、Xml配置文件的解析方式

常見xml的解析方式有三種分別是:DOM ( Document Object Model )解析方式和SAX ( Simple API for XML )解析方式,以及從JDK6.0 版本開始, JDK 開始支持的StAX ( Streaming API for XML)解析方式。在開始介紹My Batis 的XML 解析功能之前,先介紹這幾種常見的XML 處理方式。

    1.Dom解析方式:Dom解析方式是將整個文檔加載到整個內存中形成樹結構,我們很容易根據樹形結構找到兄弟、兒子、子子孫孫等節點。這樣我們就能很快找到我們想要的數據了,但是這種方式需要加載整個文檔所以會非常的耗費內存資源。

    2.SAX解析方式:SAX 是基於事件模型的XML 解析方式,它並不需要將整個XML 文檔加載到內存中,而只需將XML 文檔的一部分加載到內存中,即可開始解析,在處理過程中井不會在內存中記錄XML 中的數據,所以佔用的資源比較小。當程序處理過程中滿足條件時,也可以立即停止解析過程,這樣就不必解析剩餘的XML 內容。當SAX 解析器解析到某類型節點時,會觸發註冊在該類型節點上的回調函數,開發人員可以根據自己感興趣的事件註冊相應的回調函數。一般情況下,開發人員只需繼承SAX 提供的DefaultHandler 基類,重寫相應事件的處理方法並進行註冊即可。SAX 的缺點也非常明顯,因爲不存儲XML 文擋的結構,所以需要開發人員自己負責維護業務邏輯涉及的多層節點之間的關係,例如,某節點與其父節點之間的父子關係、與其子節點之間的父子關係。

3.StAX解析方式:

                                                    (圖片來源:MyBatis技術內幕)

三、MyBatis初始化解析mybatis-config.xml的解析方式

       MyBatis 在初始化過程中處理mybatis-config.xml 配置文件以及映射文件時,使用的是DOM解析方式,井結合使用XPath 解析XML 配置文件。正如前文所述, DOM 會將整個XML 文檔加載到內存中並形成樹狀數據結構,而XPath 是一種爲查詢XML 文檔而設計的語言,它可以與DOM 解析方式配合使用,實現對XML 文檔的解析。XPath 之於XML 就好比SQL 語言之於數據庫。

 

四、MyBatis初始化解析mybatis-config.xml的過程

前提:.類路徑下有如下xml配置文件

<employee id="${id_var}">
  <blah something="that"/>
  <first_name>Jim</first_name>
  <last_name>Smith</last_name>
  <birth_date>
    <year>1970</year>
    <month>6</month>
    <day>15</day>
  </birth_date>
  <height units="ft">5.8</height>
  <weight units="lbs">200</weight>
  <active>true</active>
</employee>

1.讀取文件並把文件轉化爲流對象

String resource = "resources/nodelet_test.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);

2.使用Xpath解析器

/*解析讀取到的資源*/
XPathParser parser = new XPathParser(inputStream, false, null, null);

我們來看一下這一行代碼所幹的事:

 /*解析資源*/
  public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
    /*初始化一些必要的參數*/
    commonConstructor(validation, variables, entityResolver);
    /*將流資源解析Dom文檔*/
    this.document = createDocument(new InputSource(inputStream));
  }

然後我們再看commonConstructor與createDocument:

(1).commonConstructor:

private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
    /*初始化校驗規則*/
    this.validation = validation;
    /*初始化實體解析器*/
    this.entityResolver = entityResolver;
    /*初始化需要解析的變量*/
    this.variables = variables;
	/*初始化一個XPath工廠*/
    XPathFactory factory = XPathFactory.newInstance();
    this.xpath = factory.newXPath();
    //xpath = new XPath() ;
  }

我們可以看到commonConstructor的主要工作就是初始化一些重要參數,爲xpath查找dom文檔做準備。

(2)createDocument:

 /*此方法的調用必須在 commonConstructor初始化以後*/
  private Document createDocument(InputSource inputSource) {
    // important: this must only be called AFTER common constructor
    try {
		//這個是DOM解析方式
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setValidating(validation);

		//名稱空間
      factory.setNamespaceAware(false);
		//忽略註釋
      factory.setIgnoringComments(true);
		//忽略空白
      factory.setIgnoringElementContentWhitespace(false);
		//把 CDATA 節點轉換爲 Text 節點
      factory.setCoalescing(false);
		//擴展實體引用
      factory.setExpandEntityReferences(true);

      DocumentBuilder builder = factory.newDocumentBuilder();
		//需要注意的就是定義了EntityResolver(XMLMapperEntityResolver),這樣不用聯網去獲取DTD,
		//將DTD放在org\apache\ibatis\builder\xml\mybatis-3-config.dtd,來達到驗證xml合法性的目的
      builder.setEntityResolver(entityResolver);
      builder.setErrorHandler(new ErrorHandler() {
        @Override
        public void error(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void fatalError(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void warning(SAXParseException exception) throws SAXException {
        }
      });
      /*至此,把一個普通流資源轉化爲一個Dom文檔流*/
      return builder.parse(inputSource);
    } catch (Exception e) {
      throw new BuilderException("Error creating document instance.  Cause: " + e, e);
    }
  }

至此,我們已經知道了MyBatis是如何加載配置文件的,接下來將會去了解Mybatis 中用Xpath到底是如何查詢Dom中的節點數據。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章