Java SE 6 做為一個開發(fā)平臺,針對不同的應用開發(fā)需求,提供了各種各樣的技術(shù)框架。XML 處理框架是 JDK 6
的重要組成部分之一。它為應用程序開發(fā)人員提供了一個統(tǒng)一的 XML 處理
API。這種框架結(jié)構(gòu)有兩個作用:一方面,開發(fā)人員透過這些框架,可以透明的替換不同廠商提供的 XML
處理服務;另一方面,服務提供商可以透過這些框架,將自己的產(chǎn)品插入到 JDK 中。這種框架一般被稱為 Service Provider
機制。Java SE 6 的 XML 處理功能分為兩個部分:XML 處理(JAXP)和 XML 綁定(JAXB)。在 XML
處理框架之上,Java SE 6 結(jié)合了注釋(Annotation)技術(shù),提供了強大的針對 Web 服務的支持。
本
文首先介紹 Service Provider 機制及其在 XML 框架中的應用。然后介紹 Java SE 6 中 XML 框架的功能,包括
SAX,StAX,DOM 三種機制。最后介紹在此基礎(chǔ)之上構(gòu)建 Web 服務的技術(shù)。JAXB 和 Web 服務的開發(fā)關(guān)系緊密,故 JAXB
的介紹也放在 Web 服務部分介紹。本文內(nèi)容基于 Java SE 6 SDK。
Service Provider 機制
對
于同一個功能,不同的廠家會提供不同的產(chǎn)品,比如不同品牌的輪胎、插頭等。在軟件行業(yè),情況也是如此。比如,對于數(shù)據(jù)的加密解密,不同的廠家使用不同的算
法,提供強度各異的不同軟件包。應用軟件根據(jù)不同的開發(fā)需求,往往需要使用不同的軟件包。每次更換不同的軟件包,都會重復以下過程:更改應用軟件代碼
-> 重新編譯 -> 測試 ->
部署。這種做法一般被稱為開發(fā)時綁定。這其實是一種比較原始的做法,缺乏靈活性和開放性。于是應用運行時綁定服務提供者的做法流行開來。具體做法是,使用
配置文件指定,然后在運行時載入具體實現(xiàn)。Java SE 平臺提供的 Service Provider
機制是折衷了開發(fā)時綁定和運行時綁定兩種方式,很好的滿足了高效和開放兩個要求。
構(gòu)成一個 Service Provider 框架需要大致三個部分,圖 1 給出了一個典型的 Service Provider 組件結(jié)構(gòu)。Java SE 平臺的大部分 Service Provider 框架都提供了 3 個主要個組件:面向開發(fā)者的 Application
接口,面向服務提供商的 Service Provider
接口和真正的服務提供者。
圖 1. Service Provider 的組件結(jié)構(gòu)
這樣做的主要好處包括:
提供了供應商的中立性,應用代碼與服務提供商完全獨立,互不依賴。應用程序開發(fā)者針對 圖 1 中 Application
接口進行開發(fā)。這個接口將不同提供商的接口差異性屏蔽掉了。無論使用哪個廠商的服務,應用程序都是針對一個穩(wěn)定、統(tǒng)一的接口開發(fā),業(yè)務邏輯和第三方組件之間有很強的獨立性。如果有需要,應用程序可以針對不同提供商重新部署。清單 1 顯示某應用程序中的一段代碼。
清單 1. 通過統(tǒng)一應用程序接口獲得服務
SAXParserFactory factory = SAXParserFactory.newInstance();
System.out.println(factory.getClass());
// Parse the input
SAXParser saxParser = factory.newSAXParser();
System.out.println(saxParser.getClass());
Output: class org.apache.xerces.jaxp.SAXParserFactoryImpl
Output: class org.apache.xerces.jaxp.SAXParserImpl
-
本例中 saxParser 的類型被聲明為 SAXParser
,但實際類型如 清單 1 中顯示,為 org.apache.xerces.jaxp.SAXParserImpl
。實際類型是由 SAXParserFactory
的靜態(tài)方法 newInstance
查找配置文件,并實例化得到的。圖 2 展示了 Java SE 6 中 XML 包的 Service Provider
的交互細節(jié)。請參考 Apache Harmony 項目的具體代碼(參見 參考資源)。
圖 2. XML 包的 Service Provider 結(jié)構(gòu)
-
提供了擴展性,更多的服務可以加入開發(fā)平臺;為了便于不同的開發(fā)商開發(fā)各自的產(chǎn)品,Java SE 平臺同時為服務提供商設(shè)計了統(tǒng)一的接口。只要提供者滿足這些接口定義(比如繼承某個接口,或者擴展抽象類),服務提供者就能被添加到 Java SE 平臺中來。以 圖 2 給出的結(jié)構(gòu)為例,服務提供者需要繼承 SAXParserFactory
、SAXParser
等抽象類,同時將類 VendorSaxParserFactoryImpl
的名字注冊到 jaxp.properties 文件中,就可以被使用了。
-
兼
顧了靈活性和效率。通過這種方式,一方面組件提供者和應用開發(fā)者開發(fā)時綁定到一個約定的接口上,另一方面載入具體哪個組件則是在運行時動態(tài)決定的。不同于
Web 服務等技術(shù)的完全動態(tài)綁定(通過運行時解析 WSDL
文件來決定調(diào)用的類型),也不是完全的編碼時綁定,這種折衷的方式提供了松耦合、高擴展性,同時也保證了可接受的效率。
XML 框架介紹
Java
SE 6 平臺提供的 XML 處理主要包括兩個功能:XML 處理(JAXP,Java Architecture XML
Processing)和 XML 綁定(JAXB,Java Architecture XML Binding)。JAXP 包括 SAX 框架
—— 遍歷元素,做出處理;DOM 框架 —— 構(gòu)造 XML 文件的樹形表示;StAX 框架 —— 拖拽方式的解析;XSLT 框架 —— 將
XML 數(shù)據(jù)轉(zhuǎn)換成其他格式。JAXB 則是負責將 XML 文件和 Java 對象綁定,在新版 JDK 中,被大量的使用在 Web 服務技術(shù)中。
SAX 框架(Simple API for XML)
SAX 全稱 Simple API for XML,該框架使用了事件處理機制來處理 XML 文件,圖 3 展示了這個過程。
圖 3. SAX 框架處理 XML 文件的流程
SAXParser 將 XML 文件當作流讀入。當 parser 遇到 Element_A,就會產(chǎn)生一個事件,然后將該事件發(fā)送給處理類。SAX 框架的一大特點是對于節(jié)點的處理是上下文無關(guān)的。比如 圖 3 中 SAXParser,允許注冊一個處理類,這個處理類對于所有節(jié)點并不加以區(qū)分,對他們的處理過程都是一致的。一般包括 startElement 和 endElement 等動作。清單 2 給出了處理類的 startElement 動作的偽代碼。
清單 2. 處理類的 startElement 動作的偽代碼
1 CLASS Listener < DefaultListener
2 PROCEDURE StartElement(…)
3 IF ( node->Obj.name == ‘Node’ )
4 // Do some calculation
5 FI
6 END
7 END
8 SAXParser->SetListener(new Listener)
使用 SAX 框架,對于 Node 節(jié)點的處理并不會根據(jù)其前驅(qū)或者后綴節(jié)點的不同而有所區(qū)別。偽代碼 3-5 行說明了這一點。一旦發(fā)現(xiàn)節(jié)點名稱是 Node
,則進行預定的處理。這個框架本身并不支持對節(jié)點進行上下文相關(guān)的處理,除非開發(fā)者另外維護一些數(shù)據(jù)結(jié)構(gòu)來記錄上下文狀態(tài)。正是由于 SAX 框架不需要記錄的狀態(tài)信息,所以運行時,SAX 框架占用的內(nèi)存(footprint)比較小,解析的速度也比較快。
DOM 框架(Document Object Model)
DOM
框架的全稱是 Document Object
Model。顧名思義,這個框架會建立一個對象模型。針對每個節(jié)點,以及節(jié)點之間的關(guān)系在內(nèi)存中生成一個樹形結(jié)構(gòu)。這個特點與 SAX
框架截然相反。需要注意的是,DOM 框架提供的對象樹模型與我們通常理解的 XML 文件結(jié)構(gòu)樹模型是有一定的區(qū)別的。圖 4 給出了一個 XML 文件的結(jié)構(gòu)。
圖 4. DOM 框架的對象模型
圖 4 中的 Element_B
具有 清單 3 這樣的結(jié)構(gòu):
清單 3. Element_B 的 XML 結(jié)構(gòu)
<Element_B>This is start of Element_B
<Node>…</Node>
This is end of Element_B
</Element_B>
按照 圖 4 和 清單 3 給出的結(jié)構(gòu),一般的對象的觀點理解,Element_B
包含子元素 Node
,而兩句話”This is start of Element_B”與”This is end of Element_B”是 Element_B
節(jié)點的內(nèi)容。而實際上,當針對 Element_B
調(diào)用 Element.getContent
,得到的是 Element_B
這個名字本身,兩句文本同 Node
一樣,也是作為子節(jié)點的。可以這樣認為,DOM 的對象模型,在內(nèi)存中模擬的是 XML 文件的物理存儲結(jié)構(gòu),而不是節(jié)點間的邏輯關(guān)系。DOM 中結(jié)點的類型也是通過 getContent
返回的節(jié)點名字符串區(qū)別的。當客戶端識別節(jié)點類型時,通常會形成以下的代碼片斷:
清單 4. 使用 DOM 框架的客戶端代碼
name = Element.getContent
SWITCH name
CASE Element_A:
// Do something
BREAK
CASE Element_B:
// Do something
BREAK
DEFAULT:
END
這種方式是很明顯的早綁定 —— 編碼時 / 編譯時綁定,而不是面向?qū)ο笳Z言中使用的運行時綁定技術(shù) —— 繼承帶來的虛擬函數(shù)特性。這個問題的產(chǎn)生和 DOM 框架的設(shè)計目標有關(guān)。DOM 的目標是一個編程語言無關(guān)的,用來處理大段復雜 XML 文件的框架(參見 參考資源),
比如書籍和文章。DOM 框架一個顯著的特征是善于處理節(jié)點與文本混合的 XML 文件(Mixed-Content Model)。這種設(shè)計使得
XML
形成樹的過程是一個直接映射,不需要進行概念上的轉(zhuǎn)換,也就節(jié)省掉很多的處理細節(jié),一定程度上提高了效率。這一點在處理大文件時比較明顯,兼顧了效率和上
下文狀態(tài)問題。另一方面,由于編程語言無關(guān)的設(shè)計目標,也決定了放棄虛函數(shù)機制是必要的。
StAX 框架(Streaming API for XML)
SAX
框架的缺點是不能記錄正在處理元素的上下文。但是優(yōu)點是運行時占內(nèi)存空間比較小,效率高。DOM 框架由于在處理 XML
時需要為其構(gòu)造一棵樹,所以特點正好相反。StAX 框架出現(xiàn)于 Java SE 6 中,它的設(shè)計目標就是要結(jié)合 SAX 框架和 DOM
框架的優(yōu)點。既要求運行時效率,也要求保持元素的上下文狀態(tài)。清單 5 是一段使用 StAX 框架處理 XML 文件的代碼。
清單 5. 使用 StAX 框架處理 XML 文件
import java.io.*;
import javax.xml.stream.*;
import javax.xml.stream.events.*;
public class StAXTest {
public static void main(String[] args) {
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
InputStream input = new ByteArrayInputStream(
("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<work-contact-info>" +
"<Location>Shanghai-shuion-333</Location>" +
"<Postal>200020</Postal>" +
"<Tel><fix>63262299</fix><mobile>1581344454</mobile></Tel>" +
"<Appellation>Mr. Wang</Appellation>" +
"</work-contact-info>").getBytes());
try {
XMLEventReader xmlEventReader = inputFactory.createXMLEventReader(input);
while (xmlEventReader.hasNext()) {
XMLEvent event = xmlEventReader.nextEvent();
if (event.isStartElement()) {
StartElement startElement = event.asStartElement();
System.out.println(startElement.getName().toString());
}
if (event.isCharacters()) {
Characters text = event.asCharacters();
if (!text.isWhiteSpace()) {
System.out.println("\t" + text.getData());
}
}
}
} catch (XMLStreamException e) {
e.printStackTrace();
}
}
}
觀察后可以發(fā)現(xiàn) StAX 框架和 SAX 框架具有相似的地方。StAX 有 Event.isStartElement
方法,SAX 有 DefaultHandler.startElement
方法。StAX 有 Event.isCharacter
方法,SAX 有 DefaultHandler.character
方法。實際上這兩個框架處理 XML 文件的時候使用了相似的模型——將 XML 文件作為元素組成的流,而不同于 DOM 的樹模型。解析 XML 文件時,應用程序調(diào)用 XMLEventReader
的 nextEvent
方法解析下一個元素(或者是解析同一個元素,根據(jù)解析的不同階段,產(chǎn)生不同元素),StAX 就會通過 XMLEventReader
產(chǎn)生一個事件。比如針對同一個元素,可能會產(chǎn)生 StartElement
和 EndElement
事件。形象的說 XMLEventReader
就像是一根繩子,拽一下,解析一個元素,產(chǎn)生一個事件。于是這種技術(shù)也被稱為”Pull Parser”技術(shù)。StAX 在處理 XML 文件時,產(chǎn)生的所有事件是通過一個 Iterator
(XMLEventReader
繼承了 Iterator
)返回的。應用程序通過這個 Iterator
能知道某個解析事件的前后分別是什么。這類信息就是一個元素的上下文信息。
XSLT 數(shù)據(jù)轉(zhuǎn)換框架(The Extensible Stylesheet Language Transformations APIs)
一
般來說 XML 文件格式被認為是一種很好的數(shù)據(jù)交換格式。于是 Java SE 6 SDK 基于以上介紹的三種 XML 處理機制,提供了一個
XML 轉(zhuǎn)換框架。XSLT 框架負責進行轉(zhuǎn)換 —— 包括將 XML 文件轉(zhuǎn)換成其他形式如 HTML,和將其他形式的文件轉(zhuǎn)換成 XML
文件。更進一步說,這個框架可以接受 DOM 作為其輸入和輸出;可以接受 SAX 解析器作為輸入或者產(chǎn)生 SAX 事件作為輸出;可以接受 I/O
Stream 作為輸入和輸出;當然也支持用戶自定義形式的輸入和輸出。圖 5 顯示了這種依賴關(guān)系:
圖 5. XSLT 框架的依賴關(guān)系
轉(zhuǎn)換框架的輸入輸出對象的類型并不要求是一一對應的。比如,使用 DOMSource
做為輸入,可以使用 StreamResult
作為輸出。清單 5 是一段偽代碼,用來顯示 JDK 將不同 javax.xml.transform.Source
轉(zhuǎn)換成不同 javax.xml.transform.Result
子類型的過程:
清單 5. JDK 轉(zhuǎn)換框架的轉(zhuǎn)換過程
// Construct input
1. factory = XMLParserDocumentFactory->NEW
2. parser = factory->NewParser
3. document = parser->Parse(File)
// Wrap input/output
4. source = Source->NEW( document )
5. sink = Result->NEW
// Construct transformer
6. tFactory = TransformerFactory->NEW
7. transformer = tFactory->NewTransformer
// Transform
8. transformer->Transfer( source, sink)
通過這個過程的轉(zhuǎn)化,一個 javax.xml.transform.Source
可以轉(zhuǎn)化成為類型 javax.xml.transform.Result
。JDK 提供了如 圖 5 所示的 4 種 Result
子類型,用戶也可以定義自己的 Result
類型。另一方面,用戶自定義的數(shù)據(jù)解析器或者數(shù)據(jù)文件,也可以作為 Transformer
的輸入。下面一個例子,針對一個數(shù)據(jù)文件,首先生成了一棵 DOM 樹,然后又根據(jù)這棵 DOM 樹,提取了所有的聯(lián)系信息,生成了一個文本文件。清單 6 給出了這個數(shù)據(jù)文件:
清單 6. 地址信息文件
work contact-info
Location Shanghai-shuion-333
Postal 200020
Tel
fix 63262299
mobile 1581344454
Appellation Mr. Wang
清單 7 為這個信息文件構(gòu)造一個 DOM 樹,并將其作為 transformer
的輸入。
清單 7. 構(gòu)造 DOM 樹
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
class ContentHandler extends DefaultHandler {
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
String name = new String(ch, start, length);
System.out.print(name + "\t");
}
}
public class DOMTest {
/**
* @param args
* @throws TransformerException
*/
public static void main(String[] args) {
try {
DocumentBuilderFactory documentfactory = DocumentBuilderFactory
.newInstance();
DocumentBuilder builder = documentfactory.newDocumentBuilder();
Document document = builder.newDocument();
Element root = document.createElement("work-contact-info");
Element loca = document.createElement("Location");
loca.setTextContent("Shanghai-shuion-333");
root.appendChild(loca);
Element postal = document.createElement("Postal");
postal.setTextContent("200020");
root.appendChild(postal);
Element tel = document.createElement("Tel");
root.appendChild(tel);
Element fix = document.createElement("fix");
fix.setTextContent("63262299");
tel.appendChild(fix);
Element mobile = document.createElement("mobile");
mobile.setTextContent("1581344454");
tel.appendChild(mobile);
Element appellation = document.createElement("Appellation");
appellation.setTextContent("Mr. Wang");
root.appendChild(appellation);
document.appendChild(root);
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer;
transformer = tFactory.newTransformer();
SAXResult result = new SAXResult();
ContentHandler cHandler = new ContentHandler();
result.setHandler(cHandler);
transformer.transform(new DOMSource(document), result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Java SE 6 SDK 提供了至少以上三種內(nèi)置的處理 XML 文件的機制。它們分別是 Simple API for
XML、Document Object Model 和 Streaming API for XML。其中 SAX 和 StAX
采用了相似的模型 —— 將 XML 文件建模為元素流,DOM 采用了樹形模型。帶來的結(jié)果是 SAX 和 StAX 運行時空間相對 DOM
緊湊。狀態(tài)保持能力則依次 SAX -> StAX -> DOM 變強。特別值得一提的是 StAX 技術(shù)是最新引進的 XML
處理技術(shù),它結(jié)合了 SAX 和 DOM 的優(yōu)點。清單 8 給出了一個粗略度量 SAX、StAX、DOM 三個框架解析同一個 XML 文件的運行效率的代碼。
清單 8. 度量 XML 解析框架的運行時間
public class StAXTest {
public static void main(String[] args) {
final String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<work-contact-info>" +
"<Location>Shanghai-shuion-333</Location>" +
"<Postal>200020</Postal>" +
"<Tel><fix>63262299</fix>" +
"<mobile>1581344454</mobile></Tel>" +
"<Appellation>Mr. Wang</Appellation>" +
"</work-contact-info>";
for (int i = 0; i < 10000; i++) {
StAX(xml);
}
for (int i = 0; i < 10000; i++) {
SAX(xml);
}
for (int i = 0; i < 10000; i++) {
DOM(xml);
}
long current = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
StAX(xml);
}
current = System.currentTimeMillis() - current;
System.out.println(current);
current = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
SAX(xml);
}
current = System.currentTimeMillis() - current;
System.out.println(current);
current = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
DOM(xml);
}
current = System.currentTimeMillis() - current;
System.out.println(current);
}
private static void StAX(final String xml) {
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
InputStream input;
try {
input = new ByteArrayInputStream(xml.getBytes());
XMLEventReader xmlEventReader = inputFactory
.createXMLEventReader(input);
while (xmlEventReader.hasNext()) {
XMLEvent event = xmlEventReader.nextEvent();
if (event.isStartElement()) {
StartElement startElement = event.asStartElement();
}
if (event.isCharacters()) {
Characters text = event.asCharacters();
if (!text.isWhiteSpace()) {
}
}
}
} catch (XMLStreamException e) {
e.printStackTrace();
}
}
private static void SAX(final String xml) {
SAXParserFactory f = SAXParserFactory.newInstance();
InputStream input;
try {
SAXParser p = f.newSAXParser();
input = new ByteArrayInputStream(xml.getBytes());
p.parse(input, new DefaultHandler());
} catch (Exception e) {
e.printStackTrace();
}
}
private static void DOM(final String xml) {
DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
InputStream input;
try {
DocumentBuilder p = f.newDocumentBuilder();
input = new ByteArrayInputStream(xml.getBytes());
p.parse(input);
} catch (Exception e) {
e.printStackTrace();
}
}
}
得出的數(shù)據(jù)如下:
2734
4953
6516
可以看出解析速度按 SAX -> StAX -> DOM 依次變慢。這組數(shù)據(jù)從一個側(cè)面反映了這三種技術(shù)的特性。SAX
處理小的,簡單的 XML 文件更高效。基于三種 XML 解析技術(shù),Java SE 6 SDK 又提供了數(shù)據(jù)格式轉(zhuǎn)換框架 —— XSLT。同時
XSLT 技術(shù)和其他很多的 JDK 框架一樣,是一個開放框架。它提供了一些抽象類和接口,讓應用程序可以根據(jù)需求,開發(fā)出不同的 XML
數(shù)據(jù)處理和轉(zhuǎn)換工具。然后通過之前敘述的 Service Provider 機制,將這些工具“插入”JDK 中。表 1 羅列了 SAX、StAX、DOM、XSLT 在 JDK 中的位置:
表 1. SAX,StAX,DOM,XSLT 在 JDK 中的位置
SAX | StAX | DOM | XSLT |
javax.xml.parsers |
javax.xml.stream |
javax.xml.parsers |
javax.xml.transform |
|
javax.xml.stream.events |
|
javax.xml.transform.dom |
|
javax.xml.stream.util |
|
javax.xml.transform.sax |
|
|
|
javax.xml.transform.stax |
|
|
|
javax.xml.transform.stream |
Web 服務
基
于 XML 的數(shù)據(jù)通常被作為 Web 服務之間互相調(diào)用的標準的數(shù)據(jù)傳輸文件格式。Java SE 6 SDK 中基于 XML
的解析技術(shù),也提供了 Web 服務的 API 支持。和較早的 JDK 5 相比,新版本的 JDK Web 服務功能更改了名稱 —— 從
JAX-RPC 變成 JAX-WS。JDK 5 只支持基于 remote-procedure-call 的 Web 服務,JDK 6
在此基礎(chǔ)上,還支持基于 SOAP message 的 Web 服務實現(xiàn)。下面將給出一個例子,基于 SOAP message,實現(xiàn)一個簡單的
Web 服務。
清單 9 給出了開發(fā)一個 Web services EndPoint 的代碼。
清單 9. 一個 Web service‘Hello’服務
package hello;
import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.xml.ws.Endpoint;
@WebService
public class Hello {
@WebMethod
public String hello(String name) {
return "Hello, " + name + "\n";
}
public static void main(String[] args) {
// create and publish an endpoint
Hello hello = new Hello();
Endpoint endpoint = Endpoint.publish("http://localhost:8080/hello", hello);
}
}
2,使用 apt 編譯 Hello.java,產(chǎn)生輔助文件:
apt -d sample example/Calculator.java
運行完這條命令之后,example 目錄下面多出了一個 jaxws 子目錄如 圖 6 所示。Apt 工具在該目錄里生成了發(fā)布 Hello Web service 所必需的兩個輔助文件。
圖 6. example 目錄
發(fā)布 Hello Web service:
java -cp sample hello.Hello
將瀏覽器指向 http://localhost:8080/hello?wsdl
會產(chǎn)生如 圖 7 所示頁面。
圖 7. 發(fā)布的 Hello Web service
Java SE 6 SDK 內(nèi)嵌了一個輕量級的 HTTP Server,方便開發(fā)者驗證簡單的 Web service 功能。通過以上三步,一個 Web service Endpoint 就部署完成,下面將開發(fā)一個調(diào)用 Hello 服務的客戶端。
為 Web 服務的客戶端產(chǎn)生存根文件:
wsimport -p sample -keep http://localhost:8080/hello?wsdl
這將會在 sample 目錄下產(chǎn)生如 圖 8 所示的文件。這一步實際是根據(jù)上面 URL 指向的 WSDL 文件,通過 JAXB 技術(shù),生成了相應的 Java 對象。
圖 8. wsimport 產(chǎn)生的文件
開發(fā),編譯,運行 Web 服務客戶程序。清單 10 給出了使用 Hello 服務的客戶程序。
清單 10. 使用 Hello 服務的客戶程序
package sample;
class HelloApp {
public static void main(String args[]) {
HelloService service = new HelloService();
Hello helloProxy = service.getHelloPort();
String hello = helloProxy.hello("developer works");
System.out.println(hello);
}
}
圖 9 是編譯并運行該客戶程序產(chǎn)生的結(jié)果:
圖 9. 調(diào)用 Hello 服務
可
以說在 Java SE 6 SDK 中,Web 服務的開發(fā)過程被大大簡化了。原來開發(fā)中需要手工重復勞動產(chǎn)生的文件,可以使用工具自動生成。比如
WSDL 文件可以自動生成,和 WSDL 綁定的 Java 對象也自動生成,部署(本文僅指 JDK 提供的輕量 HTTP server
部署環(huán)境)也大大簡化。這些全部歸功于 JDK 6 中引入的一些新的 JSR 實現(xiàn),即一些 API 和工具。表 2 給出了 JDK6 中為 Web 服務 API 提供支持的包。
表 2. JDK 中提供 Web 服務 API 支持的包
JSR | Package |
JSR 224
Java API for XML-Based Web Services 2.0
|
javax.xml.ws |
javax.xml.ws.handler |
javax.xml.ws.handler.soap |
javax.xml.ws.http |
javax.xml.ws.soap |
javax.xml.ws.spi |
JSR 222
Java Architecture for XML Binding
(JAXB) 2.0
|
javax.xml.bind |
javax.xml.bind.annotation |
javax.xml.bind.annotation.adapters |
javax.xml.bind.attachment |
javax.xml.bind.helpers |
javax.xml.bind.util |
JSR 181
Web Services Metadata for the Java
Platform
|
javax.jws |
javax.jws.soap |
除此之外 JDK 6 還提供了一些工具,包括 wsgen, wsimport 以及 Java 調(diào)用的輕量級 HTTP server。API 和工具聯(lián)合提供了一個簡單的 Web services IDE 開發(fā)環(huán)境,可以簡化 Web 服務應用的開發(fā)。
Java class 和 XML 文件的綁定
從
上一段關(guān)于 Web 服務的敘述中,我們能夠發(fā)現(xiàn)開發(fā)和部署 Web 服務的過程中存在多次 Java 對象和 XML
文件轉(zhuǎn)化的過程。比如開發(fā)和部署服務的時候,將一個 Web Service EndPoint 發(fā)布成為一個 WSDL,或者使用服務的時候,將一個
WSDL 文件轉(zhuǎn)換成一組 Java 對象。所有的轉(zhuǎn)換,都是通過工具自動完成的。這里存在一些問題,Java 對象的類型轉(zhuǎn)換成 XML
元素是需要符合一定的標準,還是隨意轉(zhuǎn)換呢?如果按照標準轉(zhuǎn)換,那么這個標準是什么樣子的?比如 Java 中的 int 類型是應該變成 <int></int>
呢,還是 <integer></integer>
。如 表 2 列出,JSR222- Java Architecture for XML Binding (JAXB) 2.0 標準為這些問題給出了一個規(guī)范的解答。
首先示范一個簡單的例子,將根據(jù)一個 XML 文件的 schema,轉(zhuǎn)換成 Java 對象。還是以 清單 6 中的數(shù)據(jù)文件為依據(jù)。構(gòu)造一個 XML schema 文件,如 清單 11 所示。要運行這個例子,首先需要下載一個 JAXB Reference Implementation jar(下載請參見 參考資源),并將該 jar 文件加入到 classpath 中。
清單 11. 用于綁定的 workcontactinfo.xsd 文件
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="workcontactinfo" type="workcontactinfo" />
<xsd:complexType name="workcontactinfo">
<xsd:sequence>
<xsd:element ref="Location" maxOccurs="1" minOccurs="1" />
<xsd:element ref="Postal" maxOccurs="1" minOccurs="1" />
<xsd:element ref="tel" maxOccurs="1" minOccurs="1" />
<xsd:element ref="Appellation" maxOccurs="1" minOccurs="1" />
</xsd:sequence>
</xsd:complexType>
<xsd:element name="tel" type="tel" />
<xsd:complexType name="tel">
<xsd:sequence>
<xsd:element ref="fix" maxOccurs="1" minOccurs="1" />
<xsd:element ref="mobile" maxOccurs="1" minOccurs="1" />
</xsd:sequence>
</xsd:complexType>
<xsd:element name="Location" type="xsd:string" />
<xsd:element name="Postal" type="xsd:string" />
<xsd:element name="Appellation" type="xsd:string" />
<xsd:element name="fix" type="xsd:string" />
<xsd:element name="mobile" type="xsd:string" />
</xsd:schema>
- 運行命令
xjc workcontactinfo.xsd
。將會在當前目錄下生成一個 generated 子目錄。
- 運行命令
javac generated\*.java
,編譯所有生成的 Java 文件。
- 操作生成的 Java 對象。清單 12 給出了一個操作生成的 java 對象的例子。要注意,一定要先將 JAXB Reference Implementation jar 放到 classpath 中。
清單 12. 調(diào)用生成的 Java 對象
import generated.*;
import java.io.FileOutputStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class JAXBTest {
public static void main(String[] args) {
try {
JAXBContext jContext = JAXBContext.newInstance("generated");
ObjectFactory factory = new ObjectFactory();
Workcontactinfo contactinfo = (Workcontactinfo) (factory
.createWorkcontactinfo());
contactinfo.setAppellation("Mr. Wang");
contactinfo.setLocation("Shanghai-shuion-333");
contactinfo.setPostal("200020");
Tel tel = (Tel) (factory.createTel());
tel.setFix("123456");
tel.setMobile("1376666666");
contactinfo.setTel(tel);
Marshaller marshaller = jContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
Boolean.TRUE);
marshaller.marshal(contactinfo, new FileOutputStream(
"workcontactinfo1.xml"));
System.out.println("java tree converted into xml & filed");
} catch (Exception e) {
e.printStackTrace();
}
}
}
運行這個程序,就能生成一個 XML 數(shù)據(jù)文件,如 清單 13。
清單 13. XML 數(shù)據(jù)文件
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<workcontactinfo>
<Location>Shanghai-shuion-333</Location>
<Postal>200020</Postal>
<tel>
<fix>123456</fix>
<mobile>1376666666</mobile>
</tel>
<Appellation>Mr. Wang</Appellation>
</workcontactinfo>
回顧一下 Web 服務的 WSDL 文件,如 圖 5
所示,是一個符合 W3C XMLSchema 規(guī)范的 schema 文件,以及整個過程中生成的 Java 類,我們應該能更好的理解 Web
服務開發(fā),部署,使用的全過程。首先 JDK 6 提供的工具 apt 根據(jù) Web Service EndPoint 中相關(guān)的注釋生成一些與
WSDL schema 綁定的 Java 類。察看這些類可以發(fā)現(xiàn),它們與 JAXB 例子中 generated 目錄下生成的 Java
文件十分相似。接著通過 HTTP 服務將這個 WSDL schema 文件發(fā)布出來。然后通過 wsimport 工具,生成一個 Web
服務的客戶運行時代理,相當于 清單 12 的功能。最終 Web 服務的用戶程序同運行時代理交互,該代理生成并傳遞形式如 清單 13 的 XML 數(shù)據(jù)文件。圖 10 結(jié)合 表 2 給出了 Web 服務開發(fā)到使用整個周期的工作流和涉及到的 JDK 包。
圖 10. Web 服務開發(fā)部署流程中 XML 技術(shù)的應用
總結(jié)
比
較前一個版本的 JDK,新版本對 XML 處理技術(shù)進行了擴展。包括新加入的 StAX 和 JAXB。基于這些新的 XML 數(shù)據(jù)處理技術(shù),JDK
6 對 Web 服務的支持也得到了大大的增強。這些增強體現(xiàn)在引入了一些注釋和 API,增加了更多的工具,可以自動化大部分開發(fā)和部署的工作。對于
XML 應用開發(fā)者來說,有更多更有力地技術(shù)可以選用。對于 Web 服務提供者來說,開發(fā)工具更為強大,開發(fā)流程更為簡化,工作效率得到提高。而對于
Web 服務的使用者,更多的底層細節(jié)被屏蔽。可以說,新版 JDK 在對 XML 相關(guān)應用開發(fā)上的支持,較前一個版本有了很大提升。
盡管千里冰封
依然擁有晴空
你我共同品味JAVA的濃香.
posted on 2007-11-17 08:52
千里冰封 閱讀(2640)
評論(2) 編輯 收藏 所屬分類:
轉(zhuǎn)載文章