千里冰封
          JAVA 濃香四溢
          posts - 151,comments - 2801,trackbacks - 0
          <2007年11月>
          28293031123
          45678910
          11121314151617
          18192021222324
          2526272829301
          2345678

          盡管千里冰封
          依然擁有晴空

           

          留言簿(204)

          隨筆分類(197)

          隨筆檔案(189)

          文章檔案(2)

          友情鏈接

          搜索

          •  

          積分與排名

          • 積分 - 966084
          • 排名 - 35

          最新隨筆

          最新評論

          閱讀排行榜

          評論排行榜

          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)
          圖 1. Service Provider 的組件結(jié)構(gòu)

          這樣做的主要好處包括:

          提供了供應商的中立性,應用代碼與服務提供商完全獨立,互不依賴。應用程序開發(fā)者針對 圖 1Application 接口進行開發(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

          1. 本例中 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)
            圖 2. XML 包的 Service Provider 結(jié)構(gòu)

          2. 提供了擴展性,更多的服務可以加入開發(fā)平臺;為了便于不同的開發(fā)商開發(fā)各自的產(chǎn)品,Java SE 平臺同時為服務提供商設(shè)計了統(tǒng)一的接口。只要提供者滿足這些接口定義(比如繼承某個接口,或者擴展抽象類),服務提供者就能被添加到 Java SE 平臺中來。以 圖 2 給出的結(jié)構(gòu)為例,服務提供者需要繼承 SAXParserFactorySAXParser 等抽象類,同時將類 VendorSaxParserFactoryImpl 的名字注冊到 jaxp.properties 文件中,就可以被使用了。

          3. 兼 顧了靈活性和效率。通過這種方式,一方面組件提供者和應用開發(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 文件的流程
          圖 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. 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)用 XMLEventReadernextEvent 方法解析下一個元素(或者是解析同一個元素,根據(jù)解析的不同階段,產(chǎn)生不同元素),StAX 就會通過 XMLEventReader 產(chǎn)生一個事件。比如針對同一個元素,可能會產(chǎn)生 StartElementEndElement 事件。形象的說 XMLEventReader 就像是一根繩子,拽一下,解析一個元素,產(chǎn)生一個事件。于是這種技術(shù)也被稱為”Pull Parser”技術(shù)。StAX 在處理 XML 文件時,產(chǎn)生的所有事件是通過一個 IteratorXMLEventReader 繼承了 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)系
          圖 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 中的位置
          SAXStAXDOMXSLT
          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 目錄
          圖 6. example 目錄

          發(fā)布 Hello Web service:

          java -cp sample hello.Hello

          將瀏覽器指向 http://localhost:8080/hello?wsdl 會產(chǎn)生如 圖 7 所示頁面。



          圖 7. 發(fā)布的 Hello Web service
          圖 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)生的文件
          圖 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 服務
          圖 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 支持的包
          JSRPackage
          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>

          1. 運行命令 xjc workcontactinfo.xsd。將會在當前目錄下生成一個 generated 子目錄。
          2. 運行命令 javac generated\*.java,編譯所有生成的 Java 文件。
          3. 操作生成的 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ù)的應用
          圖 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)載文章

          FeedBack:
          # re: Java SE 6 新特性: XML API 與 Web 服務(轉(zhuǎn))
          2007-11-18 12:57 | 專注JAVA開源
          Service Provider 應該廣泛`  回復  更多評論
            
          # re: Java SE 6 新特性: XML API 與 Web 服務(轉(zhuǎn))
          2007-11-19 10:12 | mrtsing
          不錯的文章,贊一個!  回復  更多評論
            
          主站蜘蛛池模板: 阳春市| 宜昌市| 页游| 淅川县| 安溪县| 新源县| 浦东新区| 奎屯市| 巩义市| 垫江县| 东阳市| 绵阳市| 朝阳区| 枣强县| 交口县| 卢湾区| 宁城县| 房产| 科技| 务川| 左贡县| 阿坝县| 沙洋县| 苏尼特左旗| 禄丰县| 罗平县| 山东省| 佛教| 孝感市| 阜平县| 渝中区| 遂平县| 静安区| 梓潼县| 萍乡市| 都江堰市| 河间市| 宜都市| 万州区| 吉隆县| 敦煌市|