一、XML介紹
XML 指可擴展標記語言(EXtensible Markup Language),也是一種標記語言,很類似 HTML.它的設計宗旨是傳輸數(shù)據(jù),而非顯示數(shù)據(jù)它;標簽沒有被預定義,需要自行定義標簽。
XML 被設計為具有自我描述性,是 W3C 的推薦標準,在電子計算機中,標記指計算機所能理解的信息符號,通過此種標記,計算機之間可以處理包含各種的信息比如文章等。它可以用來標記數(shù)據(jù)、定義數(shù)據(jù)類型,是一種允許用戶對自己的標記語言進行定義的源語言。它非常適合萬維網(wǎng)傳輸,提供統(tǒng)一的方法來描述和交換獨立于應用程序或供應商的結構化數(shù)據(jù)。是Internet環(huán)境中跨平臺的、依賴于內容的技術,也是當今處理分布式結構信息的有效工具。早在1998年,W3C就發(fā)布了XML1.0規(guī)范,使用它來簡化Internet的文檔信息傳輸。
xml的作用:
XML 是各種應用程序之間進行數(shù)據(jù)傳輸?shù)淖畛S玫墓ぞ?,并且在信息存儲和描述領域變得越來越流行。簡單的說,我們在開發(fā)中使用XML主要有以下兩方面應用:
a. XML做為數(shù)據(jù)交換的載體,用于數(shù)據(jù)的存儲與傳輸
b. XML做為配置文件
應用場景:
- 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5">
<servlet>
<servlet-name>HelloMyServlet</servlet-name>
<servlet-class>cn.itcast.HelloMyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloMyServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
- 存放數(shù)據(jù)
<?xml version="1.0" encoding="UTF-8"?>
<persons>
<person id="p001">
<name>張三</name>
</person>
<person id="p002">
<name>李四</name>
</person>
</persons>
二、XML語法
書寫規(guī)范
xml必須有根元素(只有一個)
xml標簽必須有關閉標簽
xml標簽對大小寫敏感
xml的屬性值須加引號
特殊字符必須轉義
xml中的標簽名不能有空格
空格/回車/制表符在xml中都是文本節(jié)點
xml必須正確地嵌套
我們將符合上述書寫規(guī)則的XML叫做格式良好的XML文檔。xml組成部分
<bookstore>
<book category="COOKING">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="CHILDREN">
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="WEB">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>

對于一個xml文件,首先必須要有根元素,該元素是所有其它元素的父元素。而在xml中所有元素形成了一棵樹。父,子及同胞等術語描述了元素之間的關系。所有的元素都可以擁有子元素。相同層級上的子元素成為同胞。
所有元素都可以擁有文本內容和屬性。
Root 根元素
Element 元素
Attribute 屬性
Text 文本
在開發(fā)中,我們將上述內容也統(tǒng)稱為Node(節(jié)點)。
接下來,我們就分析一下,對于一個xml文檔它的主要組成部分有哪些?
-
XML文檔聲明
<?xml version="1.0" encoding="UTF-8"?>- 文檔聲明必須為<?xml開頭,以?>結束;
- 文檔聲明必須從文檔的0行0列位置開始;
- 文檔聲明只有三個屬性:
a) versioin:指定XML文檔版本。必須屬性,因為我們不會選擇1.1,只會選擇1.0;
b) encoding:指定當前文檔的編碼。可選屬性,默認值是utf-8;
c) standalone:指定文檔獨立性。可選屬性,默認值為yes,表示當前文檔是獨立文檔。如果為no表示當前文檔不是獨立的文檔,會依賴外部文件。
-
元素
<servlet>- 元素是XML文檔中最重要的組成部分,
- 普通元素的結構開始標簽、元素體、結束標簽組成。例如:<hello>大家好</hello>
- 元素體:元素體可以是元素,也可以是文本,例如:<b><a>你好</a></b>
- 空元素:空元素只有開始標簽,而沒有結束標簽,但元素必須自己閉合,例如:<c/>
- 元素命名:
a) 區(qū)分大小寫
b) 不能使用空格,不能使用冒號:
c) 不建議以XML、xml、Xml開頭 - 良好的XML文檔,必須有一個根元素。
-
屬性
<web-app version="2.5">- 屬性是元素的一部分,它必須出現(xiàn)在元素的開始標簽中
- 屬性的定義格式:屬性名=屬性值,其中屬性值必須使用單引或雙引
- 一個元素可以有0~N個屬性,但一個元素中不能出現(xiàn)同名屬性
- 屬性名不能使用空格、冒號等特殊字符,且必須以字母開頭
注釋
XML的注釋與HTML相同,即以“”結束。注釋內容會被XML解析器忽略!-
轉義字符
XML中的轉義字符與HTML一樣。
因為很多符號已經(jīng)被XML文檔結構所使用,所以在元素體或屬性值中想使用這些符號就必須使用轉義字符,例如:“<”、“>”、“’”、“””、“&”。
轉義字符.png
- CDATA區(qū)
<![CDATA[
任意內容
]]>
當大量的轉義字符出現(xiàn)在xml文檔中時,會使xml文檔的可讀性大幅度降低。這時如果使用CDATA段就會好一些。
在CDATA段中出現(xiàn)的“<”、“>”、“””、“’”、“&”,都無需使用轉義字符。這可以提高xml文檔的可讀性。
在CDATA段中不能包含“]]>”,即CDATA段的結束定界符。
三、DTD約束
DTD(Document Type Definition),文檔類型定義,用來約束XML文檔。規(guī)定XML文檔中元素的名稱,子元素的名稱及順序,元素的屬性等。
開發(fā)中,我們很少自己編寫DTD約束文檔,通常情況我們都是通過框架提供的DTD約束文檔,編寫對應的XML文檔。常見框架使用DTD約束有:struts2、hibernate等。
DTD示例:
<?xml version="1.0" encoding="UTF-8"?>
<!--
模擬servlet2.3規(guī)范,如果開發(fā)人員需要在xml使用當前DTD約束,必須包括DOCTYPE。
格式如下:
<!DOCTYPE web-app SYSTEM "web-app_2_3.dtd">
-->
<!ELEMENT web-app (servlet*,servlet-mapping* , welcome-file-list?) >
<!ELEMENT servlet (servlet-name,description?,(servlet-class|jsp-file))>
<!ELEMENT servlet-mapping (servlet-name,url-pattern) >
<!ELEMENT servlet-name (#PCDATA)>
<!ELEMENT servlet-class (#PCDATA)>
<!ELEMENT url-pattern (#PCDATA)>
<!ELEMENT welcome-file-list (welcome-file+)>
<!ELEMENT welcome-file (#PCDATA)>
<!ATTLIST web-app version CDATA #IMPLIED>
案例實現(xiàn):通過提供的DTD“web-app_2_3.dtd”編寫XML
-
步驟1:創(chuàng)建web.xml文檔,并將“web-app_2_3.dtd”拷貝相同目錄下。
image.png -
步驟2:從DTD文檔開始處,拷貝需要的“文檔聲明”
image.png
image.png 步驟3:完成xml內容編寫
<web-app version="2.3">
<servlet>
<servlet-name></servlet-name>
<servlet-class></servlet-class>
</servlet>
<servlet-mapping>
<servlet-name></servlet-name>
<url-pattern></url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file></welcome-file>
<welcome-file></welcome-file>
<welcome-file></welcome-file>
</welcome-file-list>
</web-app>
DTD語法
- 文檔聲明
- 內部DTD,在XML文檔內部嵌入DTD,只對當前XML有效。
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<!DOCTYPE web-app [
... //具體的語法
]>
<web-app>
</web-app>
- 外部DTD—本地DTD,DTD文檔在本地系統(tǒng)上,公司內部自己項目使用。
<?xml version="1.0" encoding="utf-8" standalone="no" ?>
<!DOCTYPE web-app SYSTEM "web-app_2_3.dtd">
<web-app>
</web-app>
- 外部DTD—公共DTD,DTD文檔在網(wǎng)絡上,一般都有框架提供。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
</web-app>
-
元素聲明
定義元素語法:<!ELEMENT 元素名 元素描述>
元素名:自定義
元素描述包括:符號和數(shù)據(jù)類型
常見符號:? * + () | ,
常見類型:#PCDATA 表示內容是文檔,不能是子標簽
元素描述.png
<!ELEMENT web-app (servlet*,servlet-mapping* , welcome-file-list?) >
web-app 包括3個標簽,且必須順序出現(xiàn)。
servlet子標簽個數(shù)任意
servlet-mapping 子標簽個數(shù)任意
welcome-file-list 子標簽最多只能出現(xiàn)一次
<!ELEMENT servlet (servlet-name,description?,(servlet-class|jsp-file))>
servlet 有3個子標簽,且必須順序出現(xiàn)
servlet-name,必須有,且只能出現(xiàn)一次
description,可選一次
servlet-class 和 jsp-file 二選一,且只能出現(xiàn)一次
<!ELEMENT servlet-name (#PCDATA)>
servlet-name 的標簽體必須是文本
<!ELEMENT welcome-file-list (welcome-file+)>
welcome-file-list 至少有一個子標簽welcome-file
- 屬性聲明
屬性的語法:
<!ATTLIST 元素名
屬性名 屬性類型 約束
屬性名 屬性類型 約束
...
>
元素名:屬性必須是給元素添加,所有必須先確定元素名
屬性名:自定義
屬性類型:ID、CDATA、枚舉 …
ID : ID類型的屬性用來標識元素的唯一性
CDATA:文本類型
枚舉:(e1 | e2 | ...) 多選一
約束:
#REQUIRED:說明屬性是必須的;
#IMPLIED:說明屬性是可選的;
<!ATTLIST web-app version CDATA #IMPLIED>
給web-app元素添加 version屬性,屬性值必須是文本,且可選。
<web-app version="2.3"> 和 <web-app> 都符號約束
四、Schema約束
Schema是新的XML文檔約束;
Schema要比DTD強大很多,是DTD 替代者;
Schema本身也是XML文檔,但Schema文檔的擴展名為xsd,而不是xml。
Schema 功能更強大,數(shù)據(jù)類型更完善
Schema 支持名稱空間
Schema與dtd區(qū)別:
XML從SGML中繼承了DTD,并用它來定義內容的模型,驗證和組織元素。同時,它也有很多局限:
? DTD不遵守XML語法;
? DTD不可擴展;
? DTD不支持名稱空間的應用;
? DTD沒有提供強大的數(shù)據(jù)類型支持,只能表示很簡單的數(shù)據(jù)類型。
Schema完全克服了這些弱點,使得基于Web的應用系統(tǒng)交換XML數(shù)據(jù)更為容易。下面是它所展現(xiàn)的一些新特性:
? Schema完全基于XML語法,不需要再學習特殊的語法;
? Schema能用處理XML文檔的工具處理,而不需要特殊的工具;
? Schema大大擴充了數(shù)據(jù)類型,支持boolean、numbers、dates and times、URIs、integers、decimal numbers和real numbers等;
? Schema支持原型,也就是元素的繼承。如:我們定義了一個“聯(lián)系人”數(shù)據(jù)類型,然后可以根據(jù)它產(chǎn)生“朋友聯(lián)系人”和“客戶聯(lián)系”兩種數(shù)據(jù)類型;
? Schema支持屬性組。我們一般聲明一些公共屬性,然后可以應用于所有的元素,屬性組允許把元素、屬性關系放于外部定義、組合;
? 開放性。原來的DTD只能有一個DTD應用于一個XML文檔,現(xiàn)在可以有多個Schema運用于一個XML文檔。
與DTD一樣,要求可以通過schema約束文檔編寫xml文檔。常見框架使用schema的有:Spring等
實例:通過提供“web-app_2_5.xsd”編寫xml文檔
- web-app_2_5.xsd
<?xml version="1.0" encoding="UTF-8"?>
<!--
模擬servlet2.5規(guī)范,如果開發(fā)人員需要在xml使用當前Schema約束,必須包括指定命名空間。
格式如下:
<web-app xmlns="http://www.example.org/web-app_2_5"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.org/web-app_2_5 web-app_2_5.xsd"
version="2.5">
-->
<xsd:schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.org/web-app_2_5"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://www.example.org/web-app_2_5"
elementFormDefault="qualified">
<xsd:element name="web-app">
<xsd:complexType>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="servlet">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="servlet-name"></xsd:element>
<xsd:element name="servlet-class"></xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="servlet-mapping">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="servlet-name"></xsd:element>
<xsd:element name="url-pattern"></xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="welcome-file-list">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="welcome-file" maxOccurs="unbounded"></xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:choice>
<xsd:attribute name="version" type="double" use="optional"></xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:schema>
- 案例實現(xiàn)
-
步驟1:創(chuàng)建web.xml,并將“web-app_2_5.xsd”拷貝到同級目錄
image.png -
步驟2:從xsd文檔中拷貝需要的“命名空間”
image.png
image.png 完成xml內容編寫
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://www.example.org/web-app_2_5"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.org/web-app_2_5 web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name></servlet-name>
<servlet-class></servlet-class>
</servlet>
<servlet-mapping>
<servlet-name></servlet-name>
<url-pattern></url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file></welcome-file>
<welcome-file></welcome-file>
<welcome-file></welcome-file>
</welcome-file-list>
</web-app>
命名空間(語法)
如果一個XML文檔中使用多個Schema文件,而這些Schema文件中定義了相同名稱的元素時就會出現(xiàn)名字沖突。這就像一個Java文件中使用了import java.util.和import java.sql.時,在使用Date類時,那么就不明確Date是哪個包下的Date了。
總之名稱空間就是用來處理元素和屬性的名稱沖突問題,與Java中的包是同一用途。如果每個元素和屬性都有自己的名稱空間,那么就不會出現(xiàn)名字沖突問題,就像是每個類都有自己所在的包一樣,那么類名就不會出現(xiàn)沖突。
約束文檔和XML關系
當W3C提出Schema約束規(guī)范時,就提供“官方約束文檔”。我們通過官方文檔,必須“自定義schema 約束文檔”,開發(fā)中“自定義文檔”由框架編寫者提供。我們提供“自定義文檔”限定,編寫出自己的xml文檔。
聲明命名空間
默認命名空間:<xxx xmlns=””> ,使用<標簽>
顯式命名空間:<xxx xmlns:別名=””> , 使用<別名:標簽>
實例:web-app_2_5.xsd
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" …>
表示自定義schema約束文檔引用官方文檔作為顯示命名空間。如果要使用官方提供的元素或屬性,必須使用xsd前綴(自定義,此處表示官方文檔,所以使用xsd)
<xsd:schema>標簽就有官方文檔提供,默認命名空間直接使用。實例:web.xml
<web-app xmlns=http://www.example.org/web-app_2_5 …>
表示 xml 文檔引用“自定義約束文檔”作為默認命名空間
因為使用默認命名空間,<web-app>直接使用自定義約束:web-app_2_5.xsd
<xsd:schema targetNamespace=http://www.example.org/web-app_2_5
表示給當前自定義約束文檔進行起名,提供給xml文檔使用。-
xml文檔:web.xml
<web-app
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=http://www.example.org/web-app_2_5 web-app_2_5.xsdxmlns:xsi=”…” 固定寫法
表示是一個schema實例文檔,就是被schema文檔約束的xml文檔。
xsi:schemaLocation=”名稱 路徑 名稱 路徑 名稱 路徑 …”
表示用于確定當前xml文檔使用到的schema文檔的位置?!懊Q 路徑”是成對出現(xiàn),與xmlns引用命名空間對應。
五、dom4j解析
當將數(shù)據(jù)存儲在XML后,我們就希望通過程序獲得XML的內容。如果我們使用Java基礎所學習的IO知識是可以完成的,不過你需要非常繁瑣的操作才可以完成,且開發(fā)中會遇到不同問題(只讀、讀寫)。人們?yōu)椴煌瑔栴}提供不同的解析方式,并提交對應的解析器,方便開發(fā)人員操作XML。
1. 解析方式和解析器
開發(fā)中比較常見的解析方式有三種,如下:
- DOM:(Document Object Model, 即文檔對象模型) 是 W3C 組織推薦的解析XML 的一種方式。要求解析器把整個XML文檔裝載到內存,并解析成一個Document對象。
a) 優(yōu)點:元素與元素之間保留結構關系,故可以進行增刪改查操作。
b) 缺點:XML文檔過大,可能出現(xiàn)內存溢出顯現(xiàn)。 - SAX:(Simple API for XML) 不是官方標準,但它是 XML 社區(qū)事實上的標準,幾乎所有的 XML 解析器都支持它。是一種速度更快,更有效的方法。它逐行掃描文檔,一邊掃描一邊解析。并以事件驅動的方式進行具體解析,每執(zhí)行一行,都將觸發(fā)對應的事件。
a) 優(yōu)點:處理速度快,可以處理大文件
b) 缺點:只能讀,逐行后將釋放資源。 - PULL:Android內置的XML解析方式,類似SAX。(了解)
-
解析器:就是根據(jù)不同的解析方式提供的具體實現(xiàn)。有的解析器操作過于繁瑣,為了方便開發(fā)人員,有提供易于操作的解析開發(fā)包。
解析器.png 常見的解析開發(fā)包:
JAXP:sun公司提供支持DOM和SAX開發(fā)包
JDom:dom4j兄弟
jsoup:一種處理HTML特定解析開發(fā)包
dom4j:比較常用的解析開發(fā)包,hibernate底層采用。-
DOM和SAX區(qū)別
- DOM
支持回寫
會將整個XML載入內存,以樹形結構方式存儲
XML比較復雜的時候,或者當你需要隨機處理文檔中數(shù)據(jù)的時候不建議使用 - SAX
相比DOM是一種更為輕量級的方案
采用串行方法讀取 --- 逐行讀取
編程較為復雜
無法修改XML數(shù)據(jù)
- DOM
2. DOM解析原理及結構模型
XML DOM 和 HTML DOM類似,XML DOM 將 整個XML文檔加載到內存,生成一個DOM樹,并獲得一個Document對象,通過Document對象就可以對DOM進行操作。

DOM中的核心概念就是節(jié)點,在XML文檔中的元素、屬性、文本等,在DOM中都是節(jié)點!

3. API使用
如果需要使用dom4j,必須導入jar包。

dom4j 必須使用核心類SaxReader加載xml文檔獲得Document,通過Document對象獲得文檔的根元素,然后就可以操作了。
常用API如下:
- SaxReader對象
a) read(…) 加載執(zhí)行xml文檔 - Document對象
a) getRootElement() 獲得根元素 - Element對象
a) elements(…) 獲得指定名稱的所有子元素??梢圆恢付Q
b) element(…) 獲得指定名稱第一個子元素??梢圆恢付Q
c) getName() 獲得當前元素的元素名
d) attributeValue(…) 獲得指定屬性名的屬性值
e) elementText(…) 獲得指定名稱子元素的文本值
f) getText() 獲得當前元素的文本內容
節(jié)點操作
1.獲取文檔的根節(jié)點.
Element root = document.getRootElement();
2.取得某個節(jié)點的子節(jié)點.
Element element=node.element(“書名");
3.取得節(jié)點的文字
String text=node.getText();
4.取得某節(jié)點下所有名為“member”的子節(jié)點,并進行遍歷.
List nodes = rootElm.elements("member");
for (Iterator it = nodes.iterator(); it.hasNext();)
{ Element elm = (Element) it.next(); // do something}
節(jié)點對象屬性
1.取得某節(jié)點下的某屬性
Element root=document.getRootElement(); //屬性名name
Attribute attribute=root.attribute(“屬性”);//getValue()
2.取得屬性的文字
String text=attribute.getText(); === getValue();
3.取得某屬性的文字
String value=node.attributeValue(“屬性”);
解析web.xml文件:
public class Dom4jTest {
@Test
public void demo03() throws Exception{
//#1 獲得document
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(new File("src/com/yzy/mytomcat/schema/web.xml"));
//#2 獲得根元素
Element rootElement = document.getRootElement();
//打印version屬性值
String version = rootElement.attributeValue("version");
System.out.println(version);
//#3 獲得所有子元素。例如:<servlet>/<servlet-mapping>
List<Element> allChildElement = rootElement.elements();
//#4 遍歷所有
for (Element childElement : allChildElement) {
// #5.1 打印元素名
String childEleName = childElement.getName();
System.out.println(childEleName);
// #5.2 處理<servlet> ,并獲得子標簽的內容。例如:<servlet-name> 等
if("servlet".equals(childEleName)){
// 方式1:獲得元素對象,然后獲得文本
Element servletNameElement = childElement.element("servlet-name");
String servletName = servletNameElement.getText();
System.out.println("\t" + servletName);
// 方式2:獲得元素文本值
String servletClass = childElement.elementText("servlet-class");
System.out.println("\t" + servletClass);
}
// #5.3 處理<servlet-mapping> 省略...
}
}
}
4. dom4j-xpath使用
XPath 是一門在 XML 文檔中查找信息的語言
XPath 可用來在 XML 文檔中對元素和屬性進行遍歷
XPath簡化了Dom4j查找節(jié)點的過程
使用XPath必須導入jaxen-1.1-beta-6.jar否則出現(xiàn)NoClassDefFoundError: org/jaxen/JaxenException
在DOM4J中使用XPATH:
獲取所有符合條件的節(jié)點
selectNodes(String xpathExpression) 返回List集合
獲取符合條件的單個節(jié)點
selectSingleNode(String xpathExpression) 返回一個Node對象。
如果符合條件的節(jié)點有多個,那么返回第一個。
六、實例:編寫服務器軟件,訪問指定配置內容
- 1.創(chuàng)建實例工廠
編寫接口
public interface MyServlet {
public void init(); //1.初始化
public void service(); //2.執(zhí)行
public void destory(); //3.銷毀
}
實現(xiàn)接口
public class HelloMyServlet implements MyServlet {
@Override
public void init() {
System.out.println("1.初始化");
}
@Override
public void service() {
System.out.println("2.執(zhí)行中....");
}
@Override
public void destory() {
System.out.println("3.銷毀");
}
}
測試,創(chuàng)建實現(xiàn)類實例對象
public class TestApp {
@Test
public void demo01(){
//手動創(chuàng)建執(zhí)行
MyServlet myServlet = new HelloMyServlet();
myServlet.init();
myServlet.service();
myServlet.destory();
}
}
- 反射創(chuàng)建實例對象
測試程序我們直接new HelloServlet,這種編程方式我們稱為硬編碼,及代碼寫死了。為了后期程序的可擴展,開發(fā)中通常使用實現(xiàn)類的全限定類名(cn.itcast.e_web.HelloMyServlet),通過反射加載字符串指定的類,并通過反射創(chuàng)建實例。
- 反射創(chuàng)建實例對象
@Test
public void demo02() throws Exception{
/* 反射創(chuàng)建執(zhí)行
* 1) Class.forName 返回指定接口或類的Class對象
* 2) newInstance() 通過Class對象創(chuàng)建類的實例對象,相當于new Xxx();
*/
String servletClass = "com.yzy.mytomcat.web.implement.HelloMyServlet";
//3 獲得字符串實現(xiàn)類實例
Class clazz = Class.forName(servletClass);
MyServlet myServlet = (MyServlet) clazz.newInstance();
//4 執(zhí)行對象的方法
myServlet.init();
myServlet.service();
myServlet.destory();
}
- 解析xml
使用反射我們已經(jīng)可以創(chuàng)建對象的實例,此時我們使用的全限定類名,在程序是仍寫死了,我們將器配置到xml文檔中。
xml文檔內容:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://www.example.org/web-app_2_5"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.org/web-app_2_5 web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>HelloMyServlet</servlet-name>
<servlet-class>com.yzy.mytomcat.web.implement.HelloMyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloMyServlet</servlet-name>
<url-pattern>/implement</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file></welcome-file>
<welcome-file></welcome-file>
<welcome-file></welcome-file>
</welcome-file-list>
</web-app>
解析實現(xiàn)
@Test
public void demo03() throws Exception{
/* 讀取xml配置文件,獲得<servlet-class>配置的內容,取代固定字符串
*/
//1.1 加載xml配置文件,并獲得document對象
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(new File("src/com/yzy/mytomcat/schema/web.xml"));
//1.2 獲得根元素
Element rootElement = document.getRootElement();
//1.3 獲得第一個<servlet> 子元素
Element servletElement = rootElement.element("servlet");
//1.4 獲得字符串實現(xiàn)類 <servlet-class>的值
String servletClass = servletElement.elementText("servlet-class");
//3 獲得字符串實現(xiàn)類實例
Class clazz = Class.forName(servletClass);
MyServlet myServlet = (MyServlet) clazz.newInstance();
//4 執(zhí)行對象的方法
myServlet.init();
myServlet.service();
myServlet.destory();
}
- 模擬瀏覽器路徑
上面我們已經(jīng)解析xml,不過我們獲得內容是固定。我們希望如果用戶訪問的路徑是/implement,將執(zhí)行com.yzy.mytomcat.web.implement.HelloMyServlet程序,如果訪問時/implement2,將執(zhí)行com.yzy.mytomcat.web.implement.HelloMyServlet2程序。
在執(zhí)行測試程序前(@Before),解析xml文件,將解析的結果存放在Map中,map中數(shù)據(jù)的格式為:路徑=實現(xiàn)類。
image.png
- 模擬瀏覽器路徑
xml文件內容:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://www.example.org/web-app_2_5"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.org/web-app_2_5 web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>HelloMyServlet</servlet-name>
<servlet-class>com.yzy.mytomcat.web.implement.HelloMyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloMyServlet</servlet-name>
<url-pattern>/implement</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>HelloMyServlet2</servlet-name>
<servlet-class>com.yzy.mytomcat.web.implement2.HelloMyServlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloMyServlet2</servlet-name>
<url-pattern>/implement2</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file></welcome-file>
<welcome-file></welcome-file>
<welcome-file></welcome-file>
</welcome-file-list>
</web-app>
解析xml思路:先解析<servlet>,將結果存放map,name=class,然后再解析<servlet-mapping>通過name獲得class,再將url=class存放到map,最后將name=class移除。
//最終存放 key=請求路徑,value=實現(xiàn)類
private Map<String, String> data = new HashMap<String,String>();
@Before
public void demo04Before() throws Exception{
//在執(zhí)行前執(zhí)行,解析xml,并將結果存放到Map<路徑,實現(xiàn)類>中
//1 獲得document
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(new File("src/com/yzy/mytomcat/schema/web.xml"));
//2 獲得根元素
Element rootElement = document.getRootElement();
//3 獲得所有的子元素 <servlet> 、<servlet-mapping>等
List<Element> allChildElement = rootElement.elements();
/* 4 遍歷所有
* 1)解析到<servlet>,將其子標簽<servlet-name>與<servlet-class>存放到Map中
* 2)解析到<servlet-mapping>,獲得子標簽<servlet-name>和<url-pattern>, 從map中獲得1的內容,組合成 url = class 鍵值對
*/
for (Element childElement : allChildElement) {
//4.1 獲得元素名
String eleName = childElement.getName();
//4.2 如果是servlet,將解析內容存放到Map中
if("servlet".equals(eleName)){
String servletName = childElement.elementText("servlet-name");
String servletClass = childElement.elementText("servlet-class");
data.put(servletName, servletClass);
}
//4.3 如果是servlet-mapping,獲得之前內容,組成成key=url,value=class并添加到Map中
if("servlet-mapping".equals(eleName)){
String servletName = childElement.elementText("servlet-name");
String urlPattern = childElement.elementText("url-pattern");
// 獲得<servlet-name>之前存放在Map中<servlet-class>值
String servletClass= data.get(servletName);
// 存放新的內容 url = class
data.put(urlPattern, servletClass);
// 將之前存放的數(shù)據(jù)刪除
data.remove(servletName);
}
//打印信息
System.out.println(data);
}
}
模擬瀏覽器請求路徑,通過url從map獲得class,并使用反射執(zhí)行實現(xiàn)類。
@Test
public void demo04() throws Exception{
//1 模擬路徑
//String url = "/implement";
String url = "/implement2";
//2 通過路徑獲得對應的實現(xiàn)類
String servletClass = data.get(url);
//3 獲得字符串實現(xiàn)類實例
Class clazz = Class.forName(servletClass);
MyServlet myServlet = (MyServlet) clazz.newInstance();
//4 執(zhí)行對象的方法
myServlet.init();
myServlet.service();
myServlet.destory();
}
- 瀏覽器訪問
使用Socket編寫服務,通過瀏覽器可以訪問,并解析瀏覽器發(fā)送的請求數(shù)據(jù),最終獲得請求路徑。
訪問路徑:
http://localhost:8888/implement
http://localhost:8888/implement2
- 瀏覽器訪問
@Test
public void demo05() throws Exception{
//使用socket獲得請求路徑
//1.1 給本地計算機綁定端口8888
ServerSocket serverSocket = new ServerSocket(8888);
//1.2 程序阻塞,等待瀏覽器請求。
Socket accept = serverSocket.accept();
//1.3 獲得請求所有數(shù)據(jù)
BufferedReader reader = new BufferedReader(new InputStreamReader(accept.getInputStream()));
//1.4 獲得第一行數(shù)據(jù),請求行,例如:GET /hello HTTP/1.1
String firstLine = reader.readLine();
//1.5 請求行三部分數(shù)據(jù)由空格連接,獲得中間數(shù)據(jù)。表示請求路徑
String url = firstLine.split(" ")[1];
System.out.println(url);
//2 通過路徑獲得對應的實現(xiàn)類
String servletClass = data.get(url);
//3 獲得字符串實現(xiàn)類實例
Class clazz = Class.forName(servletClass);
MyServlet myServlet = (MyServlet) clazz.newInstance();
//4 執(zhí)行對象的方法
myServlet.init();
myServlet.service();
myServlet.destory();
//5 釋放資源
reader.close();
控制臺等待鏈接時:

輸入網(wǎng)址測試,控制臺效果:













