1. 程式人生 > >1.使用dom4j解析xml檔案,模擬伺服器解析web.xml

1.使用dom4j解析xml檔案,模擬伺服器解析web.xml

1. XML文件的構成

XML文件結構其實與html程式碼結構非常相似。

1. XML文件宣告
  1. 文件宣告必須以<?xml 開頭,以?>結束。
  2. 文件宣告必須從文件的0行0列開始。
  3. 文件宣告只有三個屬性:
    • version:指定XML文件版本,必選,一般使用1.0
    • encoding:指定XML文件編碼。可選,預設utf-8
    • standalone:指定XML是否獨立存在(而不依賴於外部DTD或schema檔案),一般不設定此屬性
2. XML文件元素(element)
  1. 最基本的xml元素由開始標籤、元素體、結束標籤組成。例如:
  2. 元素的開始標籤中可以含有一個或多個屬性。例如:
  3. 元素中可以嵌入元素。例如:
  4. 自閉合標籤被稱為空標籤,一般只有屬性而沒有值。例如:
  5. 元素體中如果需要輸出特殊字元例如引號(")、大括號(>)、等已經被文件元素引用的字元,就需要使用轉義字元。
    • 如果文件中有較多的轉義字元,會給閱讀帶來很大的不便,這時候可以使用CDATA區。CDATA區的字元會被文件處理器自動處理。使用方法:
    <![CDATA[
        你的內容
    ]]>
3. 註釋

XML文件註釋與html相同,以“<!--”開頭,並以 “-->”結束。

4. 文件約束

因為XML文件中可以編寫任何內容,但編寫文件時必須要使用正確的標籤,使用者才能讀取到有用的資訊。這時可以使用約束來規定文件的語法。常用的例如hibernate、Mybatis的dtd約束,以及spring所使用的schema約束。

2. dom4j解析

hibernate框架底層解析xml文件使用的就是dom4j技術,它具有具有效能優異、靈活性好、功能強大和極端易用的特點。
dom4j的功能十分強大,對於xml節點的增刪改查都有涉及,我們這裡就非常非常簡單地模擬一下servlet的對於路徑的匹配,也算是拋個磚頭。
web.xml:

<?xml version="1.0" encoding="utf-8"?>
<web-app version="2.5">
    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.uuunl.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>worldServlet</servlet-name>
        <servlet-class>com.uuunl.servlet.WorldServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>worldServlet</servlet-name>
        <url-pattern>/world</url-pattern>
    </servlet-mapping>
</web-app>

上述程式碼中有兩個servlet,一個用來對映“/hello”,一個用來對映“/world”。當我們的伺服器接收到一個請求,它會把路徑名和引數解析好了之後根據我們的設定來決定對映到哪一個servlet中。例如“http:www.uuunl.com/hello?name=tom”這裡路徑名就是“/hello”,引數為“name”,它的值為“tom”。當然,實際情況中比我們這裡要複雜得多。

以下是非常非常簡單的模擬伺服器解析我們的web.xml檔案與匹配servlet路徑的過程:

private static String mappingUrl(String url) {
    // 讀取web.xml檔案
    File file = new File(Main.class.getResource("/web.xml").getPath());
    try {
       // 利用讀取到的檔案初始化 SAXReader 物件,並獲得 Document 的根物件
        Document document = new SAXReader().read(file);
        Element rootElement = document.getRootElement();
        // 初始化需要匹配到的 servletName
        String servletNameString = "";
        // 獲取所有的 servlet-mapping 節點
        List<Element> servletMappingElementList = rootElement.elements("servlet-mapping");
        for (Element ele : servletMappingElementList) {
            // 獲取 servlet-mapping 節點的文字
            String urlPatternString = ele.element("url-pattern").getText();
            // 如果 servlet-mapping 中有 url-pattern 的文字值與傳入的url相同,則取出
            if (url.equals(urlPatternString)) {
                servletNameString = ele.element("servlet-name").getText();
            }
            // 否則報錯
            if (servletNameString.trim().equals("")) {
                throw new RuntimeException("無法找到匹配的路徑名");
            }
        }
        // 獲取所有的 servlet 節點
        List<Element> servletElementList = rootElement.elements("servlet");
        for (Element ele : servletElementList) {
            // 獲取 servlet 中對應的 servlet-class 的文字值
            if (servletNameString.equals(ele.element("servlet-name").getText())) {
                return ele.element("servlet-class").getText();
            }
        }
    } catch (DocumentException e) {
        throw new RuntimeException(e.getMessage());
    }
    return null;
}

當傳入一個url,以上方法會將返回匹配到的class名稱輸出,當伺服器得到class名稱,就可以使用反射來建立相應的物件,處理相應請求。