mybatis 基礎支持層解析文件

在初始化過程當中處理mybatis-config.xml配置文件中,使用DOM解析,並結合XPath解析XML配置文件。java

上面三個接口是jdk中的,mybatis封裝了它們。web

XMLMapperEntityResolver 實現了 EntityResolver 用做離線加載DTD文檔即以下所示的sql

避免聯網加載致使緩慢express

其中的resolveEntity方法入參解釋以下:網絡

DTD聲明始終以!DOCTYPE開頭,空一格後跟着文檔根元素的名稱,若是是內部DTD,則再空一格出現[],在中括號中是文檔類型定義的內容. 而對於外部DTD,則又分爲私有DTD與公共DTD,私有DTD使用SYSTEM表示,接着是外部DTD的URL. 而公共DTD則使用PUBLIC,接着是DTD公共名稱,接着是DTD的URL
doctype 屬性可返回與文檔相關的文檔類型聲明(Document Type Declaration)。
systemId: 外部資源(多半是DTD)的URI,好比本地文件file:///usr/share/dtd/somefile.dtd或者網絡某個地址的文件http://www.w3.org/somefile.dtd;

publicId: systemId已經能夠表示任何位置的外部DTD資源了,可是它是直接指向相應的資源,publicId的做用在於其間接性。
publicID就至關於一個名字,這個名字表明瞭一個外部資源。
好比,咱們規定」W3C HTML 4.01″這個字符串對應」http://www.w3.org/somedir/somefile.dtd」這個資源。
那麼,publicID=」W3C HTML 4.01″ 和 systemID=」http://www.w3.org/somedir/somefile.dtd」是同樣的,
兩者都引用了http://www.w3.org/somedir/somefile.dtd做爲該文檔的外部DTD。
對於以DTD爲Schema的XML文件
<!DOCTYPE   web-app   PUBLIC   "-//Sun   Microsystems,   Inc.//DTD   Web   Application   2.3//EN "   "http://java.sun.com/dtd/web-app_2_3.dtd ">

publicId是-//Sun   Microsystems,   Inc.//DTD   Web   Application   2.3//EN
SystemId是http://java.sun.com/dtd/web-app_2_3.dtdmybatis

 

調用app

讀取dtd文檔造成InputSource對象。ide

 

在XPathParser中ui

以下構造方法:this

先調用

而後再調用

加載xml文檔

其中builder.parse(inputSource)是加載xml文件:調用以下方法

PropertyParser.parse方法以下:

使用了GenericTokenParser建立對象,處理佔位符格式爲‘${}’:

GenericTokenParser爲通用字佔位符解析器

如下爲其parse方法的邏輯:

public String parse(String text) {
        if (text == null || text.isEmpty()) {
            return "";
        }
        // search open token 從openToken佔位符開始標記開始查
        int start = text.indexOf(openToken, 0);
        if (start == -1) {
            return text;
        }
        char[] src = text.toCharArray();
        int offset = 0;
        final StringBuilder builder = new StringBuilder();
        StringBuilder expression = null;
        while (start > -1) {
            if (start > 0 && src[start - 1] == '\\') {
                // this open token is escaped. remove the backslash and continue.
                //遇到了轉義的開始標記,移除轉移標記並將其拼接好放入builder中
                builder.append(src, offset, start - offset - 1).append(openToken);
                offset = start + openToken.length();//修改offset偏移量的位置移到openToken以後
            } else {
                // found open token. let's search close token.
                //開始查找佔位符開始標記,但沒轉義標誌
                if (expression == null) {
                    expression = new StringBuilder();
                } else {
                    expression.setLength(0);//將這個expression大小改成0長度
                }
                //將前面的字符串追加Bulider中
                builder.append(src, offset, start - offset);
                offset = start + openToken.length();//修改offset偏移量的位置移到openToken以後
                int end = text.indexOf(closeToken, offset);//從offset以後開始找closeToken索引位置
                while (end > -1) {//找到closeToken
                    if (end > offset && src[end - 1] == '\\') {//除以轉義結束標記同上openToken方法
                        // this close token is escaped. remove the backslash and continue.
                        expression.append(src, offset, end - offset - 1).append(closeToken);
                        offset = end + closeToken.length();//修改offset偏移量的位置移到closeToken以後
                        end = text.indexOf(closeToken, offset);//修改end索引位置到closeToken以後
                    } else {
                        //將開始標記和結束標記之間的字符串追加到expression中保存
                        expression.append(src, offset, end - offset);
                        offset = end + closeToken.length();//修改offset的索引位置到closeToken以後
                        break;
                    }
                }
                if (end == -1) {
                    // close token was not found.
                    //沒有找到結束標記closeToken就添加到builder中範圍是從start即openToken
                    builder.append(src, start, src.length - start);
                    offset = src.length;
                } else {
                    //將佔位符字面值交給handleToken處理,並將處理直接追加到builderz中保存
                    //最終拼湊出解析後的完整內容
                    builder.append(handler.handleToken(expression.toString()));
                    offset = end + closeToken.length();//修改offset的索引位置到closeToken以後
                }
            }
            start = text.indexOf(openToken, offset);//移動start索引位置到openToken以後
        }
        if (offset < src.length) {//這裏的offset是0則就是把0到src.length總體長度給追加進去
            builder.append(src, offset, src.length - offset);
        }
        return builder.toString();
    }
}

 

TokenHandler接口共有四個實現類

其中上面的

使用了PropertyParser中的一個私有靜態內部類VariableTokenHandler

其中的handleToken方法:

@Override
public String handleToken(String content) {
    if (variables != null) {//<properties>節點下定義的鍵值對,用於替換佔位符,檢測本集合是否爲空
        String key = content;
        if (enableDefaultValue) {//是否支持佔位符中使用默認值功能
            final int separatorIndex = content.indexOf(defaultValueSeparator);//查找到指定佔位符和默認值之間分隔符
            String defaultValue = null;
            if (separatorIndex >= 0) {//如有分隔符
                key = content.substring(0, separatorIndex);//獲取佔位符名稱即key
                defaultValue = content.substring(separatorIndex + defaultValueSeparator.length());//獲取默認值即value
            }
            if (defaultValue != null) {//如有默認值
                return variables.getProperty(key, defaultValue);//在variables集合中查找指定佔位符
            }
        }
        if (variables.containsKey(key)) {//不支持佔位符中使用默認值功能
            return variables.getProperty(key);//直接查找variables集合
        }
    }
    return "${" + content + "}";//variables集合是空值處理
}

舉個例子:

${username:root},:就爲分隔符,則PropertyParser會解析後使用username在variables集合中相對應的值若無則使用root爲username的默認值

GenericTokenParser 只是處理轉義標誌和查找佔位符,具體解析有其指定的    TokenHandler實現,(默認值和動態sql解析)

相關文章
相關標籤/搜索