程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 用Java客戶機調用Web服務: J2SE和J2EE環境中Web服務客戶機簡介

用Java客戶機調用Web服務: J2SE和J2EE環境中Web服務客戶機簡介

編輯:關於JAVA

Web 服務的力量在於互操作性。由於業界在 Web 服務技術方面(SOAP、WSDL、UDDI)的協作,更具體地說,是由於 Web 服務互操作性組織(Web Services Interoperability organization,WS-I.org)的工作,Web 服務才可以與其他的 Web 服務進行交互,而不管 Web 服務開發和運行在哪一個平台上(比如是 Microsoft .NET 還是 IBM WebSphere)。Web 服務客戶機分為多種類型,比如另一個 Web 服務、用腳本語言編寫的客戶機、C# 客戶機、Java 客戶機等等。本文重點講解 Java 客戶機,它可以用於訪問任何遵循 Web 服務規范的 Web 服務(不僅僅是 Java Web 服務)。通過閱讀本文,您將了解到需要用來通過不同的查找和訪問方法調用相同的 Web 服務的 Web 服務客戶機代碼。本文所用的示例是“Hello” Web 服務,它提供了“getGreeting”操作。這種操作接受一個字符串參數(例如Jane),然後返回一句問候語“Hello Jane!”。

Web 服務角色

這一部分將描述調用 Web 服務的過程。Web 服務提供者用 Web 服務描述語言(Web Services Description Language,WSDL)文檔來描述 Web 服務。Web 服務一般發布到統一描述、發現和集成(Universal Description, Discovery and Integration,UDDI)注冊中心。Web 服務請求者在 UDDI 注冊中心查找 Web 服務,綁定到 Web 服務,然後調用它。Web 服務角色顯示在 圖1中。本文將重點講解從服務請求者到服務提供者的水平箭頭(綁定)。本文將把請求者稱為 客戶機,它也可以稱為 消費者。

用於

Java 技術標准的開發隨著 Java 規范提案(Java Specification Request,JSR)提交給Java Community Process(JCP)而出現。兩個 JSR 涵蓋了 Java Web 服務體系結構:

JSR 101: 用於基於 XML 的 RPC 的 Java API(Java API for XML based RPC,JAX-RPC)

JSR 109: 實現企業 Web 服務(Implementing Enterprise Web services)

兩個規范提供了廠商的實現的一致性和互操作性需求。

JAX-RPC —— Java 到 XML 和 XML 到 Java 映射 API

JAX-RPC 為基於 XML 的遠程過程調用(Remote Procedure Call,RPC)和 Java 應用程序編程接口(Java Application Programming Interface,API):

WSDL 到 Java 和 Java 到 WSDL 映射:例如,將 WSDL 端口類型映射到 Java 服務端點接口(Java Service Endpoint Interface,SEI)。

XML 數據類型到 Java 數據類型和 Java 數據類型到 XML 數據類型映射,包括簡單類型、復雜類型和數組。

除了 XML 映射之外,JAX-RPC 還定義了服務器端編程模型和 API,我將在後面的部分中更詳細地介紹它。AX-RPC 1.1 根據 Web 服務互操作性組織(Web Services Interoperability organization,WS-I)基本概要版本 1.0(Basic Profile version 1.0)添加了互操作性需求。

JSR 109 —— J2EE 環境的 API

JSR 109 指定了 Java 2 Enterprise Edition(J2EE)環境的 Web 服務編程模型和體系結構。JSR 109 構建在 SOAP 1.1 和 WSDL 1.1 的基礎上,它涵蓋了 J2EE 環境中 JAX-RPC 的使用( 圖 2)。它還定義了 J2EE 應用程序服務器中的部署模型。JSR 109 的客戶端編程模型(我將在下面的幾個部分中介紹)符合 JAX-RPC。

JAX-RPC 1.1 和 JSR 109 是 J2EE 1.4 的組成部分。

服務查找

有兩種客戶機,它們在代碼的編寫、打包和調用的方式上都不相同:

非受管客戶機

J2EE 容器管理的客戶機

這裡,非受管意指不是 J2EE 容器管理的。這些客戶機是 Java 2 Standard Edition(J2SE)客戶機,它們是通過簡單的 java 命令進行調用的。對於非受管客戶機,服務查找是通過 JAX-RPC ServiceFactory 進行的,JAX-RPC ServiceFactory 是創建服務訪問點的工廠。對於 J2EE 容器管理的客戶機,服務查找是通過 JNDI 查找進行的。

ServiceFactory
JSR 101:“JAX-RPC ServiceFactory 是在 J2SE 環境中查找 Web 服務的標准方式。”

JAX-RPC ServiceFactory

JAX-RPC ServiceFactory 是一個抽象類,用作實例化 JAX-RPC Service 的工廠。它是廠商無關的,使您能夠編寫可移植代碼。ServiceFactory 是實例化的,可以如下進行使用: javax.xml.rpc.Service service = ServiceFactory.newInstance().createService(...);

您需要將 Web 服務的全限定名(也就是名稱空間加上服務名稱)傳送到 createService() 方法和(可選)描述您想要查找的 Web 服務的 WSDL URL。步驟如下:

(可選)指定 WSDL URL。

指定 Web 服務的全限定名。

調用 ServiceFactory 的 createService() 方法。

然後可以使用獲得的服務接口(Service Interface)來獲取存根、動態代理、或 DII Call 對象,如“ 服務訪問”部分所述。在該部分中,同時還描述了動態調用接口(Dynamic Invocation Interface,DII)。使用這種方法,您不需要知道您想要調用的 Web 服務的 WSDL URL,您只需要指定 createService() 方法中的服務名稱參數。清單1展示了如何使用 ServiceFactory 實現 JAX-RPC Service 。QName 是一個 javax.xml.namespace.QName 。

清單 1. 使用 JAX-RPC ServiceFactory 獲得 JAX-RPC 服務

String wsdlURL = http://localhost:6080/HelloWebService/services/Hello?wsdl";
String namespace = "http://Hello.com";
String serviceName = "HelloWebService";
QName serviceQN = new QName(namespace, serviceName);

ServiceFactory serviceFactory = ServiceFactory.newInstance();
/* The "new URL(wsdlURL)" parameter is optional */
Service service = serviceFactory.createService(new URL(wsdlURL), serviceQN);

由特定於廠商的 JAX-RPC ServiceFactory 可供選擇。如果您想要利用某個廠商的存根,使用這些 JAX-RPC ServiceFactory 通常是非常容易的(客戶機代碼編寫起來很簡單)。然而,這樣的擴展不是標准的,將很可能在其他廠商的 J2EE 實現上不起作用。

JNDI 查找

JSR 109:“JNDI 查找是在 J2EE 環境中查找 Web 服務的標准方式。”

JNDI 服務查找

J2EE 容器管理的客戶機被打包成 Enterprise Archives(.EAR)文件,並且在 J2EE 容器中運行。除了 Java 代碼之外,描述符也打包在該歸檔文件中。下面是幾個不同類型的 J2EE 容器管理的客戶機:

應用程序客戶機容器客戶機

Web 容器客戶機:JavaBean 或 Servlet

EJB 容器客戶機:EJB

JAX-RPC 定義了受管客戶機的編程模型,而 JSR 109(“實現企業 Web 服務(Implementing Enterprise Web services)”)定義了 J2EE 容器受管的客戶機的編程模型。JSR 109 的目標之一就是它的客戶機編程模型遵循 JAX-RPC。然而,JSR 109 並沒有推薦使用 JAX-RPC ServiceFactory 。相反,它建議客戶機使用 Java 命名和目錄接口(Java Naming and Directory Interface,JNDI)來獲取服務接口(Service Interface)。這個過程包括下面兩個步驟,同時在 清單2中進行了舉例說明:

實例化本地 JNDI 上下文。

在此上下文中對 Web 服務進行 JNDI 查找。

清單 2. JNDI 服務查找

Context ic = new InitialContext();
Service service = (Service) ctx.lookup("java:comp/env/service/HelloService");

Web 服務的名稱(在本例中為 java:comp/env/service/HelloService )是在客戶機應用程序的部署描述符中指定的。JSR 109 建議把服務引用的所有邏輯名組織在 service 子目錄中。如果客戶機環境上下文是 java:comp/env ,您就可以以下面的代碼結束:

service name in context =
client environment context + "service" subcontext + service name.

在本例中,上下文中的服務名為:

java:comp/env/ + service/ + HelloService.

service 子上下文(subcontext) + 服務名(例如 service/HelloService )也稱為邏輯上的服務名,是在 Web 服務客戶機應用程序的部署描述符中進行聲明的。

JNDI 查找返回 JAX-RPC Service Interface。J2EE 容器確保在部署描述符中指定的綁定通用 JAX-RPC Service 的實現。您也可以將該查找返回的對象強制轉換成您的 Web 服務的指定接口。這示於 清單3,其中 HelloService 擴展了通用的 JAX-RPC Service 接口。

清單 3. 可供選擇的 JNDI 查找

Context ic= new InitialContext();
HelloServiceInterface service =
   (HelloServiceInterface) ic.lookup("java:comp/env/service/HelloService");

然後可以使用獲得的服務接口(Service Interface)來獲取靜態存根、動態代理或 DII Call 對象,如下面的“ 服務訪問”部分所述。

服務訪問

在前面的部分中,您看到了 JAX-RPC ServiceFactory 用作 JAX-RPC Services 的工廠。同樣地,JAX-RPC Service 也用作代理和存根的工廠。一旦您實例化了服務,就擁有了三種訪問和調用 Web 服務的方法:

存根

動態代理

動態調用接口(Dynamic Invocation Interface,DII)

存根和動態代理方法使用服務端點接口(Service Endpoint Interface,SEI)。它基本上是 WSDL 端口類型元素中描述 Web 服務操作的 Java 表示。它是定義 Java 客戶機用來與 Web 服務進行交互的方法的 Java 接口。SEI 是由從 WSDL 到 Java 的映射工具(比如 Apache Axis 的 Java2WSDL 或 IBM WSDK 的 WSDL2Client)生成的。

SEI

服務端點接口(Service Endpoint Interface,SEI)是 WSDL A  is the Java representation of a WSDL port type.

存根

存根方法使用在從 WSDL 到 Java 映射階段運行之前創建的特定於平台的存根。因為存根是在運行之前創建的,所以它有時稱為 靜態存根。它是一個實現 SEI 的 Java 類。從 WSDL 到 Java 的映射工具生成所需的客戶端構件;該工具主要導入 WSDL 服務定義,然後創建相應的 Java 代碼。構件包括 SEI、存根、(可選)Holder、序列化器、反序列化器和實用程序類。JAX-RPC 建議把存根的實例綁定到特定的協議和傳輸上,比如 SOAP 綁定存根。對於存根方法,需要執行的步驟如下:

獲取一個 JAX-RPC 服務。

獲得一個存根。

在該存根上調用 Web 服務的操作。

步驟2和3顯示在 清單4中。請注意,使用 JAX-RPC Service 的 getPort 方法(在下一部分中進行描述)來獲取存根也是有可能的。

清單 4. 通過存根訪問 Web 服務

Hello myStub = (Hello) service.getHello();
System.out.println(myStub.getGreeting("Jane");

此方法的優勢在於它的簡單性。基本上指需要兩行代碼來訪問和調用 Web 服務的操作。然而,您需要知道開發時的 WSDL URL 並且運行您的從 WSDL 到 Java 的映射工具。另外,這些存根不是可移植的,因為它們依賴於實現類,並且不應該作為應用程序的一部分進行打包。可移植存根的設計超出了 JAX-RPC 1.0 和 1.1 的范圍。

動態代理

您可以使用代理從 JAX-RPC Service中調用 Web 服務的操作。代理是實現 SEI 的 Java 類。獲得代理使用的是 JAX-RPC Service 的getPort() 方法,它接受您想要調用的 Web 服務的端口的名稱(存在於 WSDL 文檔中)以及代理實現的 SEI。它之所以稱為 動態是因為該代理是在運行時創建的。動態代理客戶機的步驟如下:

獲取一個 JAX-RPC Service 。

使用 JAX-RPC Service 的 getPort() 方法來獲得一個代理以調用 Web 服務的操作。

在步驟1中,對於受管客戶機,通過把 WSDL URL 以及 Web 服務名參數傳送到 createService() 方法來獲得 JAX-RPC Service 。對於 J2EE 容器管理的客戶機,您通過 JNDI 查找來獲取 JAX-RPC Service 。清單5展示了在 Web 服務上調用“getGreeting”操作的動態代理方法(步驟2)。

清單 5. 在動態代理上調用 Web 服務的操作

String namespace = "http://Hello.com";
String portName = "Hello";
QName portQN = new QName(namespace, portName);

Hello myProxy = service.getPort(portQN, Hello.class);
System.out.println(myProxy.getGreeting("Jane"));

這是所有您為了使用動態代理方法調用 Web 服務而需要編寫的代碼。使用這種方法的優勢在於您可以編寫可移植的、廠商無關的代碼。然而,您需要知道開發時的 WSDL URL,並且需要在運行之前根據 WSDL 文檔運行您的從 WSDL 到 Java 的映射工具。如果您沒有這方面的信息,或者 WSDL URL 很可能改變,那麼您應該改為使用 DII 方法。

動態調用接口(DII)

JAX-RPC Call 接口支持動態調用 Web 服務的操作。使用這種方法,您不需要知道開發時的 WSDL URL。JAX-RPC Service 用作實例化 JAX-RPC Call 的工廠,而不是從 JAX-RPC Service 中獲得代理。此方法的步驟如下:

獲取一個 JAX-RPC Service 。

使用 JAX-RPC Service 的 createCall() 方法實例化 JAX-RPC Call 。

使用它的 setter 方法來配置您的 Call 實例。

使用 JAX-RPC Call 的調用方法來調用 Web 服務的操作。

在步驟1中,對於受管客戶機,僅僅通過把 Web 服務(而非 WSDL URL)的名稱傳送到 createService() 方法來從 JAX-RPC ServiceFactory 中獲取 JAX-RPC Service 。對於 J2EE 容器管理的客戶機,您通過 JNDI 查找來獲取 JAX-RPC Service 。在步驟3中,配置參數為:操作的名稱、端口號、目標服務端點的地址、返回類型。查閱 JAX-RPC 規范的8.2.4.2節可以獲得標准的特性集的信息。步驟2到4示於 清單6中。

清單 6. 使用 DII 方法調用 Web 服務

String namespace = "http://Hello.com";
String portName = "Hello";
QName portQN = new QName(namespace, portName);
String operationName = "getGreeting";
Call call = service.createCall();
call.setPortTypeName(portQN);
call.setOperationName(new QName(namespace, operationName));
call.setProperty(Call.ENCODINGSTYLE_URI_PROPERTY, "");
call.setProperty(Call.OPERATION_STYLE_PROPERTY, "wrapped");
call.addParameter("param1", <xsd:string>,ParameterMode.IN);
call.setReturnType(<xsd:string>);
Object[] inParams = new Object[] {"Jane"};
String ret = (String) call.invoke(inParams);

您可以重用 Call 實例來調用 Web 服務上的其他操作。

注意:createCall() 和 addParameter() 方法有其他的簽名。剛才描述的並不是調用它們的惟一方法。例如,使用端口類型名和操作名參數來調用 createCall() 也是可能的。

通過 Call 對象進行 DII 調用的過程比使用存根活動態代理復雜。然而,使用 DII Call 接口的優勢在於,客戶機可以調用遠程過程而無需知道開發時的 WSDL URI 或 Web 服務操作的簽名。這樣當 Web 服務的細節改變時,很容易對代碼進行修改。使用 DII 客戶機,不需要像動態代理或靜態存根的情形那樣由從 WSDL 到 Java 的映射工具(Emitter)生成運行時類。然而,如果您知道您想要調用的 Web 服務不可能更改,就應該使用動態代理,因為配置 Call 實例可能很復雜。

動態發現和調用(DDI)

動態發現和調用(Dynamic Discovery and Invocation,DDI)是 Web 服務的靈活性使用的極至,其中,Web 可以動態發現和調用 Web 服務而無需預先知道它。雖然在 前面的部分中描述的 DII 客戶機不需要知道 Web 服務的開發時細節,但是它們不涉及發現 Web 服務的過程。DDI 客戶機執行三個步驟:

發現 UDDI 中 Web 服務的細節:查找提供服務的業務,然後查找描述該服務的 WSDL 文檔的 URL。

讀取 WSDL 文檔來查找 Web 服務上的信息:名稱空間、服務、端口和參數。

調用服務。

在步驟1中,UDDI Registry Enquiry API 用於浏覽 UDDI 注冊中心。在步驟2中,UDDI4J API 用於解析 WSDL 文檔。最後,在步驟3中,使用 DII 方法(在 前面的部分中進行了描述)。要獲得關於 DDI 的信息,您最好讀一讀 developerWorks 文章“Dynamic Discovery and Invocation of Web services”(列在 參考資料部分)。

結束語

本文描述了編寫 Java Web 服務客戶機代碼的不同方式。如前所述,為了構建和運行所提供的客戶機代碼可能會需要一些構件(例如 JAX-RPC 或 JSR 109 類庫、Java2WSDL 映射工具發出的存根、服務端點接口(Service Endpoint Interface,SEI)、部署描述符等等)。另外,還需要准備和運行“Hello” Web 服務實現。請查閱 Zip 歸檔文件中的 Readme 文件以獲取關於如何構建和運行類的說明。

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