程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java SE 6新特性: XML API與Web服務

Java SE 6新特性: XML API與Web服務

編輯:關於JAVA

2006 年底,Sun 公司發布了 Java Standard Edition 6(Java SE 6)的最終正式版,代號 Mustang(野馬)。跟 Tiger(Java SE 5)相比,Mustang 在性能方面有了不錯的提升。與 Tiger 在 API 庫方面的大幅度加強相比,雖然 Mustang 在 API 庫方面的新特性顯得不太多,但是也提供了許多實用和方便的功能:在 腳本,XML 和 Web 服務,編譯器 API,數據庫,JMX,網絡 和 Instrumentation 方面都有不錯的新特性和功能加強。

本系列 文章主要介紹 Java SE 6 在 API 庫方面的部分新特性,通過一些例子和講解,幫助開發者在編程實踐當中更好的運用 Java SE 6,提高開發效率。本文是系列文章的最後一篇,主要介紹了 Java SE 6 中提供的 XML 處理框架,以及在此框架之上結合注釋(Annotation) 技術,所提供的強大的針對 Web 服務的支持。

Java SE 6 做為一個開發平台,針對不同的應用開發需求,提供了各種各樣的技術框架。XML 處理框架是 JDK 6 的重要組成部分之一。它為應用程序開發人員提供了一個統一的 XML 處理 API。這種框架結構有兩個作用:一方面,開發人員透過這些框架,可以透明的替換不同廠商提供的 XML 處理服務;另一方面,服務提供商可以透過這些框架,將自己的產品插入到 JDK 中。這種框架一般被稱為 Service Provider 機制。Java SE 6 的 XML 處理功能分為兩個部分:XML 處理(JAXP)和 XML 綁定(JAXB)。在 XML 處理框架之上,Java SE 6 結合了注釋(Annotation)技術,提供了強大的針對 Web 服務的支持。

本文首先介紹 Service Provider 機制及其在 XML 框架中的應用。然後介紹 Java SE 6 中 XML 框架的功能,包括 SAX,StAX,DOM 三種機制。最後介紹在此基礎之上構建 Web 服務的技術。JAXB 和 Web 服務的開發關系緊密,故 JAXB 的介紹也放在 Web 服務部分介紹。本文內容基於 Java SE 6 SDK。

Service Provider 機制

對於同一個功能,不同的廠家會提供不同的產品,比如不同品牌的輪胎、插頭等。在軟件行業,情況也是如此。比如,對於數據的加密解密,不同的廠家使用不同的算法,提供強度各異的不同軟件包。應用軟件根據不同的開發需求,往往需要使用不同的軟件包。每次更換不同的軟件包,都會重復以下過程:更改應用軟件代碼 -> 重新編譯 -> 測試 -> 部署。這種做法一般被稱為開發時綁定。這其實是一種比較原始的做法,缺乏靈活性和開放性。於是應用運行時綁定服務提供者的做法流行開來。具體做法是,使用配置文件指定,然後在運行時載入具體實現。Java SE 平台提供的 Service Provider 機制是折衷了開發時綁定和運行時綁定兩種方式,很好的滿足了高效和開放兩個要求。

構成一個 Service Provider 框架需要大致三個部分,圖 1 給出了一個典型的 Service Provider 組件結構。Java SE 平台的大部分 Service Provider 框架都提供了 3 個主要個組件:面向開發者的 Application 接口,面向服務提供商的 Service Provider 接口和真正的服務提供者。

圖 1. Service Provider 的組件結構

這樣做的主要好處包括:

提供了供應商的中立性,應用代碼與服務提供商完全獨立,互不依賴。應用程序開發者針對 圖 1 中 Application 接口進行開發。這個接口將不同提供商的接口差異性屏蔽掉了。無論使用哪個廠商的服務,應用程序都是針對一個穩定、統一的接口開發,業務邏輯和第三方組件之間有很強的獨立性。如果有需要,應用程序可以針對不同提供商重新部署。清單 1 顯示某應用程序中的一段代碼。

清單 1. 通過統一應用程序接口獲得服務

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 的靜態方法 newInstance 查找配置文件,並實例化得到的。圖 2 展示了 Java SE 6 中 XML 包的 Service Provider 的交互細節。請參考 Apache Harmony 項目的具體代碼(參見 參考資源)。

圖 2. XML 包的 Service Provider 結構

提供了擴展性,更多的服務可以加入開發平台;為了便於不同的開發商開發各自的產品,Java SE 平台同時為服務提供商設計了統一的接口。只要提供者滿足這些接口定義(比如繼承某個接口,或者擴展抽象類),服務提供者就能被添加到 Java SE 平台中來。以 圖 2 給出的結構為例,服務提供者需要繼承 SAXParserFactory、SAXParser 等抽象類,同時將類 VendorSaxParserFactoryImpl 的名字注冊到 jaxp.properties 文件中,就可以被使用了。

兼顧了靈活性和效率。通過這種方式,一方面組件提供者和應用開發者開發時綁定到一個約定的接口上,另一方面載入具體哪個組件則是在運行時動態決定的。不同於 Web 服務等技術的完全動態綁定(通過運行時解析 WSDL 文件來決定調用的類型),也不是完全的編碼時綁定,這種折衷的方式提供了松耦合、高擴展性,同時也保證了可接受的效率。

XML 框架介紹

Java SE 6 平台提供的 XML 處理主要包括兩個功能:XML 處理(JAXP,Java Architecture XML Processing)和 XML 綁定(JAXB,Java Architecture XML Binding)。JAXP 包括 SAX 框架 —— 遍歷元素,做出處理;DOM 框架 —— 構造 XML 文件的樹形表示;StAX 框架 —— 拖拽方式的解析;XSLT 框架 —— 將 XML 數據轉換成其他格式。JAXB 則是負責將 XML 文件和 Java 對象綁定,在新版 JDK 中,被大量的使用在 Web 服務技術中。

SAX 框架(Simple API for XML)

SAX 全稱 Simple API for XML,該框架使用了事件處理機制來處理 XML 文件,圖 3 展示了這個過程。

圖 3. SAX 框架處理 XML 文件的流程

SAXParser 將 XML 文件當作流讀入。當 parser 遇到 Element_A,就會產生一個事件,然後將該事件發送給處理類。SAX 框架的一大特點是對於節點的處理是上下文無關的。比如 圖 3 中 SAXParser,允許注冊一個處理類,這個處理類對於所有節點並不加以區分,對他們的處理過程都是一致的。一般包括 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 節點的處理並不會根據其前驅或者後綴節點的不同而有所區別。偽代碼 3-5 行說明了這一點。一旦發現節點名稱是 Node,則進行預定的處理。這個框架本身並不支持對節點進行上下文相關的處理,除非開發者另外維護一些數據結構來記錄上下文狀態。正是由於 SAX 框架不需要記錄的狀態信息,所以運行時,SAX 框架占用的內存(footprint)比較小,解析的速度也比較快。

DOM 框架(Document Object Model)

DOM 框架的全稱是 Document Object Model。顧名思義,這個框架會建立一個對象模型。針對每個節點,以及節點之間的關系在內存中生成一個樹形結構。這個特點與 SAX 框架截然相反。需要注意的是,DOM 框架提供的對象樹模型與我們通常理解的 XML 文件結構樹模型是有一定的區別的。圖 4 給出了一個 XML 文件的結構。

圖 4. DOM 框架的對象模型

圖 4 中的 Element_B 具有 清單 3 這樣的結構:

清單 3. Element_B 的 XML 結構

<Element_B>This is start of Element_B
  <Node>…</Node>
  This is end of Element_B
</Element_B>

按照 圖 4 和 清單 3 給出的結構,一般的對象的觀點理解,Element_B 包含子元素 Node,而兩句話”This is start of Element_B”與”This is end of Element_B”是 Element_B 節點的內容。而實際上,當針對 Element_B 調用 Element.getContent,得到的是 Element_B 這個名字本身,兩句文本同 Node 一樣,也是作為子節點的。可以這樣認為,DOM 的對象模型,在內存中模擬的是 XML 文件的物理存儲結構,而不是節點間的邏輯關系。DOM 中結點的類型也是通過 getContent 返回的節點名字符串區別的。當客戶端識別節點類型時,通常會形成以下的代碼片斷:

清單 4. 使用 DOM 框架的客戶端代碼

name = Element.getContent
SWITCH name
CASE Element_A:
  // Do something
  BREAK
CASE Element_B:
  // Do something
  BREAK
DEFAULT:
END

這種方式是很明顯的早綁定 —— 編碼時 / 編譯時綁定,而不是面向對象語言中使用的運行時綁定技術 —— 繼承帶來的虛擬函數特性。這個問題的產生和 DOM 框架的設計目標有關。DOM 的目標是一個編程語言無關的,用來處理大段復雜 XML 文件的框架(參見 參考資源),比如書籍和文章。DOM 框架一個顯著的特征是善於處理節點與文本混合的 XML 文件(Mixed-Content Model)。這種設計使得 XML 形成樹的過程是一個直接映射,不需要進行概念上的轉換,也就節省掉很多的處理細節,一定程度上提高了效率。這一點在處理大文件時比較明顯,兼顧了效率和上下文狀態問題。另一方面,由於編程語言無關的設計目標,也決定了放棄虛函數機制是必要的。

StAX 框架(Streaming API for XML)

SAX 框架的缺點是不能記錄正在處理元素的上下文。但是優點是運行時占內存空間比較小,效率高。DOM 框架由於在處理 XML 時需要為其構造一棵樹,所以特點正好相反。StAX 框架出現於 Java SE 6 中,它的設計目標就是要結合 SAX 框架和 DOM 框架的優點。既要求運行時效率,也要求保持元素的上下文狀態。清單 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();
        }
    }
 }

觀察後可以發現 StAX 框架和 SAX 框架具有相似的地方。StAX 有 Event.isStartElement 方法,SAX 有 DefaultHandler.startElement 方法。StAX 有 Event.isCharacter 方法,SAX 有 DefaultHandler.character 方法。實際上這兩個框架處理 XML 文件的時候使用了相似的模型——將 XML 文件作為元素組成的流,而不同於 DOM 的樹模型。解析 XML 文件時,應用程序調用 XMLEventReader 的 nextEvent 方法解析下一個元素(或者是解析同一個元素,根據解析的不同階段,產生不同元素),StAX 就會通過 XMLEventReader 產生一個事件。比如針對同一個元素,可能會產生 StartElement 和 EndElement 事件。形象的說 XMLEventReader 就像是一根繩子,拽一下,解析一個元素,產生一個事件。於是這種技術也被稱為”Pull Parser”技術。StAX 在處理 XML 文件時,產生的所有事件是通過一個 Iterator(XMLEventReader 繼承了 Iterator)返回的。應用程序通過這個 Iterator 能知道某個解析事件的前後分別是什麼。這類信息就是一個元素的上下文信息。

XSLT 數據轉換框架(The Extensible Stylesheet Language Transformations APIs)

一般來說 XML 文件格式被認為是一種很好的數據交換格式。於是 Java SE 6 SDK 基於以上介紹的三種 XML 處理機制,提供了一個 XML 轉換框架。XSLT 框架負責進行轉換 —— 包括將 XML 文件轉換成其他形式如 HTML,和將其他形式的文件轉換成 XML 文件。更進一步說,這個框架可以接受 DOM 作為其輸入和輸出;可以接受 SAX 解析器作為輸入或者產生 SAX 事件作為輸出;可以接受 I/O Stream 作為輸入和輸出;當然也支持用戶自定義形式的輸入和輸出。圖 5 顯示了這種依賴關系:

圖 5. XSLT 框架的依賴關系

轉換框架的輸入輸出對象的類型並不要求是一一對應的。比如,使用 DOMSource 做為輸入,可以使用 StreamResult 作為輸出。清單 5 是一段偽代碼,用來顯示 JDK 將不同 javax.xml.transform.Source 轉換成不同 javax.xml.transform.Result 子類型的過程:

清單 5. JDK 轉換框架的轉換過程

// 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)

通過這個過程的轉化,一個 javax.xml.transform.Source 可以轉化成為類型 javax.xml.transform.Result。JDK 提供了如 圖 5 所示的 4 種 Result 子類型,用戶也可以定義自己的 Result 類型。另一方面,用戶自定義的數據解析器或者數據文件,也可以作為 Transformer 的輸入。下面一個例子,針對一個數據文件,首先生成了一棵 DOM 樹,然後又根據這棵 DOM 樹,提取了所有的聯系信息,生成了一個文本文件。清單 6 給出了這個數據文件:

清單 6. 地址信息文件

work contact-info
Location  Shanghai-shuion-333
  Postal  200020
  Tel
    fix    63262299
    mobile  1581344454
  Appellation  Mr. Wang

清單 7 為這個信息文件構造一個 DOM 樹,並將其作為 transformer 的輸入。

清單 7. 構造 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 提供了至少以上三種內置的處理 XML 文件的機制。它們分別是 Simple API for XML、Document Object Model 和 Streaming API for XML。其中 SAX 和 StAX 采用了相似的模型 —— 將 XML 文件建模為元素流,DOM 采用了樹形模型。帶來的結果是 SAX 和 StAX 運行時空間相對 DOM 緊湊。狀態保持能力則依次 SAX -> StAX -> DOM 變強。特別值得一提的是 StAX 技術是最新引進的 XML 處理技術,它結合了 SAX 和 DOM 的優點。清單 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();
        }
    }
 }

得出的數據如下:

2734
4953
6516

可以看出解析速度按 SAX -> StAX -> DOM 依次變慢。這組數據從一個側面反映了這三種技術的特性。SAX 處理小的,簡單的 XML 文件更高效。基於三種 XML 解析技術,Java SE 6 SDK 又提供了數據格式轉換框架 —— XSLT。同時 XSLT 技術和其他很多的 JDK 框架一樣,是一個開放框架。它提供了一些抽象類和接口,讓應用程序可以根據需求,開發出不同的 XML 數據處理和轉換工具。然後通過之前敘述的 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 的數據通常被作為 Web 服務之間互相調用的標准的數據傳輸文件格式。Java SE 6 SDK 中基於 XML 的解析技術,也提供了 Web 服務的 API 支持。和較早的 JDK 5 相比,新版本的 JDK Web 服務功能更改了名稱 —— 從 JAX-RPC 變成 JAX-WS。JDK 5 只支持基於 remote-procedure-call 的 Web 服務,JDK 6 在此基礎上,還支持基於 SOAP message 的 Web 服務實現。下面將給出一個例子,基於 SOAP message,實現一個簡單的 Web 服務。

清單 9 給出了開發一個 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);
  }
}

使用 apt 編譯 Hello.java,產生輔助文件:

apt -d sample example/Calculator.java

運行完這條命令之後,example 目錄下面多出了一個 jaxws 子目錄如 圖 6 所示。Apt 工具在該目錄裡生成了發布 Hello Web service 所必需的兩個輔助文件。

圖 6. example 目錄

發布 Hello Web service:

java -cp sample hello.Hello

將浏覽器指向 http://localhost:8080/hello?wsdl 會產生如 圖 7 所示頁面。

圖 7. 發布的 Hello Web service

Java SE 6 SDK 內嵌了一個輕量級的 HTTP Server,方便開發者驗證簡單的 Web service 功能。通過以上三步,一個 Web service Endpoint 就部署完成,下面將開發一個調用 Hello 服務的客戶端。

為 Web 服務的客戶端產生存根文件:

wsimport -p sample -keep http://localhost:8080/hello?wsdl

這將會在 sample 目錄下產生如 圖 8 所示的文件。這一步實際是根據上面 URL 指向的 WSDL 文件,通過 JAXB 技術,生成了相應的 Java 對象。

圖 8. wsimport 產生的文件

開發,編譯,運行 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 是編譯並運行該客戶程序產生的結果:

圖 9. 調用 Hello 服務

可以說在 Java SE 6 SDK 中,Web 服務的開發過程被大大簡化了。原來開發中需要手工重復勞動產生的文件,可以使用工具自動生成。比如 WSDL 文件可以自動生成,和 WSDL 綁定的 Java 對象也自動生成,部署(本文僅指 JDK 提供的輕量 HTTP server 部署環境)也大大簡化。這些全部歸功於 JDK 6 中引入的一些新的 JSR 實現,即一些 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 調用的輕量級 HTTP server。API 和工具聯合提供了一個簡單的 Web services IDE 開發環境,可以簡化 Web 服務應用的開發。

Java class 和 XML 文件的綁定

從上一段關於 Web 服務的敘述中,我們能夠發現開發和部署 Web 服務的過程中存在多次 Java 對象和 XML 文件轉化的過程。比如開發和部署服務的時候,將一個 Web Service EndPoint 發布成為一個 WSDL,或者使用服務的時候,將一個 WSDL 文件轉換成一組 Java 對象。所有的轉換,都是通過工具自動完成的。這裡存在一些問題,Java 對象的類型轉換成 XML 元素是需要符合一定的標准,還是隨意轉換呢?如果按照標准轉換,那麼這個標准是什麼樣子的?比如 Java 中的 int 類型是應該變成 <int></int> 呢,還是 <integer></integer>。如 表 2 列出,JSR222- Java Architecture for XML Binding (JAXB) 2.0 標准為這些問題給出了一個規范的解答。

首先示范一個簡單的例子,將根據一個 XML 文件的 schema,轉換成 Java 對象。還是以 清單 6 中的數據文件為依據。構造一個 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" />
|-------10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
      <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. 調用生成的 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 數據文件,如 清單 13。

清單 13. XML 數據文件

<?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 規范的 schema 文件,以及整個過程中生成的 Java 類,我們應該能更好的理解 Web 服務開發,部署,使用的全過程。首先 JDK 6 提供的工具 apt 根據 Web Service EndPoint 中相關的注釋生成一些與 WSDL schema 綁定的 Java 類。察看這些類可以發現,它們與 JAXB 例子中 generated 目錄下生成的 Java 文件十分相似。接著通過 HTTP 服務將這個 WSDL schema 文件發布出來。然後通過 wsimport 工具,生成一個 Web 服務的客戶運行時代理,相當於 清單 12 的功能。最終 Web 服務的用戶程序同運行時代理交互,該代理生成並傳遞形式如 清單 13 的 XML 數據文件。圖 10 結合 表 2 給出了 Web 服務開發到使用整個周期的工作流和涉及到的 JDK 包。

圖 10. Web 服務開發部署流程中 XML 技術的應用

總結

比較前一個版本的 JDK,新版本對 XML 處理技術進行了擴展。包括新加入的 StAX 和 JAXB。基於這些新的 XML 數據處理技術,JDK 6 對 Web 服務的支持也得到了大大的增強。這些增強體現在引入了一些注釋和 API,增加了更多的工具,可以自動化大部分開發和部署的工作。對於 XML 應用開發者來說,有更多更有力地技術可以選用。對於 Web 服務提供者來說,開發工具更為強大,開發流程更為簡化,工作效率得到提高。而對於 Web 服務的使用者,更多的底層細節被屏蔽。可以說,新版 JDK 在對 XML 相關應用開發上的支持,較前一個版本有了很大提升。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved