關鍵詞:
JAX-RPC ??
web服務 ?? ??????????????????????????????????????
閱讀本文前您需要以下的知識和工具:
- JavaTM Web Services Developer Pack 1.1,并且會使用初步使用;
- 遼倩崾褂靡恢諩JB容器來開發、部署EJB,并且了解怎么在客戶端訪問EJB組件;
- 對Apache axis Web服務開發工具有基本的了解;
- 基本的Java編程知識。
如果使用JAX-RPC開發Web服務,我們幾種選擇:
- Servlet作為Web服務端點;
- 無狀態會話Bean作為Web服務端點;
- 基于消息(如JMS)的應用程序作為Web服務端點。
本文以Servlet作為Web服務端點的情況來介紹JAX-RPC Web服務開發,關于本篇文章中案例的介紹詳見本系列文章第一篇: 《用JAXM開發Web服務》。
本文的參考資料見 參考資料
本文的全部代碼在這里 下載
JAX-RPC快速入門
JAX-RPC,Java? API for XMLbased RPC,顧名思義,它是一種遠程方法調用(或者說遠程過程調用),那么它和其它的遠程方法調用(RPC, COM,CORBA,RMI)有什么區別呢?我們看一般的遠程方法調用的結構,如圖1所示。

綜合比較常用的遠程方法調用技術,它們有以下的共性:
- 在客戶端和服務端有通用編程接口;
- 在客戶端有Stub,在服務端有Tie(有的叫Skeleton);
- 客戶端和服務端有專門的協議進行數據傳輸。
對于通用接口的描述,比如CORBA有IDL of CORBA,Java RMI有Java RMI interface in RMI,對于XMLbased RPC來說,IDL就是WSDL(Web服務描述語言)。那么XMLbased RPC來說,什么是這個結構中的"傳輸協議",當然是SOAP,SOAP消息通過以傳輸文本為基礎的協議(HTTP、SMTP、FTP)為載體來使用的,也就是說,SOAP消息的傳輸建立在HTTP、SMTP、FTP傳輸協議之上。
JAX-RPC的構架如下。

從上圖可以看出,客戶端調用的是JAX-RPC服務端點(Service Endpoint),這個服務端點是通過WSDL語言描述的。在這個體系結構中,對于客戶端,可以是JS2E、J2ME或者J2EE平臺運行環境;對于服務端,可以是J2EE1.3或者J2EE1.4容器(Servlet容器或者EJB容器)。Apache axis是一個很好的JAX-RPC運行環境實現,同時也提供了優秀的開發工具,本文將使用它進行開發。
使用Servlet作為服務端點,本案例的基本構架如下圖所示。

客戶端通過SOAP消息和JAX-RPC服務端交互,JAX-RPC服務端運行在Servlet容器中,它通過調用EJB容器中的EJB組件來處理具體的業務邏輯。
使用JAX-RPC開發Web服務,可以按照以下的步驟進行:
- 服務端點定義;
- 服務描述;
- 服務端點實現;
- 服務端點部署;
- 服務發布和發現。
注意:對于服務的發布和發現,由于機制比較復雜,本文不討論,可能會在本系列文章進行專題討論。
開發快速入門
一個完整的JAX-RPC開發實例,將按照上面的5個步驟進行,但是我們也可以使用非常簡單的方式來發布一個Web服務。在介紹我們的案例前,讓我們用一分鐘快速開發一個Web服務。
首先安裝好JWSDP,你可以從 http://java.sun.com/webservices下載。
把本案例源代碼中的\src\bookservice.ear\web.war目錄拷貝到%JWSDP_HOME%\webapps目錄下,web.war文件里已經包括了Apache axis運行環境。
在%JWSDP_HOME%\classes目錄下新建一個HelloWorld.java文件,它的代碼如下:
例程1 最簡單的Web服務HelloWorld
package com.hellking.webservice;
public class HelloWorld
{
public String sayHello(String name)
{
return "Hello! "+name;
}
}
|
編譯這個類,然后編輯%JWSDP_HOME%\webapps\WEB-INF\server-config.wsdd文件,找到記,在其后面加入以下內容:
在瀏覽器例輸入:
http://localhost:8080/web/services/HelloWorld?wsdl如果出現部署WSDL描述文件,那么最簡單的Web服務已經部署成功!
下面我們使用最簡單的方式來調用這個Web服務,在瀏覽器里輸入:
http://localhost:8080/web/services/HelloWorld?wsdl&method=sayHello&name=hellking那么在瀏覽器將會顯示以下內容:
例程2 在瀏覽器里調用Web服務
如果結果是這樣,那么最簡單的Web服務已經部署成功,并且測試也通過了。注意"Hello!hellking"是調用Web服務返回的結果,它是我們期望的。下面我們來看一個完整的Web服務開發的例子。
服務端點定義
服務端點定義的工作主要是確定"服務定義接口"(Service Definition Interface),有時也叫Web服務端點接口(Web services endpoint interface)。服務端點定義有兩中方法獲得:
- 使用某些工具從WSDL文件獲得;
- 直接使用Java語言編寫。
Apache axis提供了從WSDL文件中獲得Web服務端點的工具。您可以這樣使用:
java org.apache.axis.wsdl.WSDL2Java (WSDL-file-URL)
|
使用這個命令前先設置好以下的環境變量,后面的介紹中還會使用axis工具,它們也要這樣設置環境變量:
SET AXIS_HOME=
set CLASSPATH=%CLASSPATH%;
%AXIS_HOME%/axis-1_1/lib/axis.jar;
%AXIS_HOME%/axis-1_1/lib/jaxrpc.jar;
%AXIS_HOME%/axis-1_1/lib/saaj.jar;
%AXIS_HOME%/axis-1_1/lib/commons-logging.jar;
%AXIS_HOME%/axis-1_1/lib/commons-discovery.jar;
%AXIS_HOME%/axis-1_1/lib/wsdl4j.jar;.
|
關于WSDL2Java的更詳細的使用,請參考Apache axis的User Guides( http://ws.apache.org/axis/)。
我們這里直接使用Java編寫服務端點接口的方法。在本案例中,定義了三個業務方法,它們分別是查找所有的圖書、按書名查找圖書、按類別查找圖書。那么安照這三個業務方法,可以定義出以下的服務端點接口:
例程3 服務端點定義(BookServiceInterface.java)
package com.hellking.webservice.servlet;
/**
*@author hellking
*/
import java.util.Collection;
import com.hellking.webservice.BookVO;
public interface BookServiceInterface
{
/**
* @return Vector
*/
public Collection getAllBooks();//查找所有的圖書
/**
* @param name
* @return BookVO
*/
public BookVO getTheBookDetail(String name);//按照書名查找圖書
/**
* @return Collection
*/
public Collection getBookByCategory(String category);//按類別查找
}
|
上面代碼中的BookVO是一個序列化的對象,它有以下屬性,每個屬性都提供了getter和setter方法。
例程4 BookVO的部分代碼
public class BookVO implements java.io.Serializable
{
private String name;
private String publisher;
private float price;
private String isbn;
private String description;
private String category;
private Collection authors;
public void setName(String name)
{
this.name=name;
}
public String getName()
{
return this.name;
}
…
}
|
編譯好這兩個類。
服務端點描述
可以使用Java2WSDL從以上定義的服務端點接口中獲得服務描述(WSDL文件)。使用Apache axis工具,只要使用以下命令即可:
java org.apache.axis.wsdl.Java2WSDL -o temp.wsdl
-l"http://localhost:8080/axis/services/BookServletService"
-n "urn:BookServletService"
-p"com.hellking.webservice" "urn:BookServletService"
com.hellking.webservice.servlet.BookServiceInterface
|
以上命令的解釋:
-o:生成的WSDL文件;
-l:Web服務的位置;
-n:這個WSDL文件的名字空間;
-p:包到名字空間的映射;
最后一個參數是Web服務端點接口。
使用以上命令后,將生成一個名為temp.wsdl Web服務描述文。
服務端點實現
有了服務描述文件,就可以使用它來生成JAX-RPC 的框架,這個框架使得我們編程變得簡單,當然您也可以直接編寫實現代碼,然后部署,但是那樣編程會變得困難。
使用以下的命令就可以生成這個框架:
java org.apache.axis.wsdl.WSDL2Java -o . -d Session -s -S true
-Nurn:BookServletService com.hellking.webservice.servlet temp.wsdl
|
使用這個命令后將生成以下文件:
BookServiceInterface.java:新的BookServiceInterface接口,它擴展了java.rmi.Remote接口;
BookServiceInterfaceService.java:客戶端服務接口,用來獲得BookServiceInterface對象的引用;
BookServiceInterfaceServiceLocator.java:在客戶端使用,主要用來服務定位;
BookServletServiceSoapBindingImpl.java:服務端實現類,它實現了BookServiceInterface接口,服務端的業務方法實現代碼就在這里編寫;
BookServletServiceSoapBindingSkeleton.java:服務端Skeleton;
BookServletServiceSoapBindingStub.java:客戶端Stub;
BookVO.java:新的BookVO序列化對象;
deploy.wsdd:部署這個Web服務的腳本;
undeploy.wsdd:卸載這個Web服務的腳本。
服務端點實現類的基本框架已經生成出來了,我們的任務就是往里面增加具體的業務內容。下面我們來看具體的服務端點的實現。如例程3所示。
例程5 服務端點實現類
package com.hellking.webservice.servlet;
import java.util.*;
import javax.naming.*;
import com.hellking.webservice.ejb.*;
public class BookServletServiceSoapBindingImpl
implements com.hellking.webservice.servlet.BookServiceInterface{
InitialContext init=null;
BookServiceFacadeHome facadeHome;
public BookServletServiceSoapBindingImpl()
{
try
{
init=new InitialContext();
}
catch(Exception e)
{
}
}
//業務方法,查找所有的圖書
public java.lang.Object[] getAllBooks() throws java.rmi.RemoteException {
System.out.println("getAllBooks");
try
{
Object objref = init.lookup("ejb/bookfacade");
facadeHome = (BookServiceFacadeHome)javax.rmi.PortableRemoteObject.narrow(
objref, BookServiceFacadeHome.class);
Collection result=facadeHome.create().getAllBook();
System.out.println(result.size());
Object[] ret=new Object[result.size()];
Iterator it=result.iterator();
int i=0;
while(it.hasNext())
{
ret[i++]=it.next();
}
// System.out.println(((BookVO)ret[0]).getName());
return ret;
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
}
//業務方法,按書名查找圖書
public com.hellking.webservice.BookVO getTheBookDetail(java.lang.String in0)
throws java.rmi.RemoteException {
com.hellking.webservice.BookVO ret=null;
try
{
Object objref = init.lookup("ejb/bookfacade");
facadeHome = (BookServiceFacadeHome)javax.rmi.PortableRemoteObject.narrow(
objref, BookServiceFacadeHome.class);
Collection result=facadeHome.create().getBookDetail(in0);
Iterator it=result.iterator();
while(it.hasNext())
{
ret=( com.hellking.webservice.BookVO)it.next();
}
}
catch(Exception e)
{
}
return ret;
}
//業務方法,按類別查找圖書
public java.lang.Object[] getBookByCategory(java.lang.String in0)
throws java.rmi.RemoteException {
try
{
Object objref = init.lookup("ejb/bookfacade");
facadeHome = (BookServiceFacadeHome)javax.rmi.PortableRemoteObject.narrow(
objref, BookServiceFacadeHome.class);
System.out.println(in0);
Collection result=facadeHome.create().findByCategory(in0);
Object[] ret=new Object[result.size()];
Iterator it=result.iterator();
int i=0;
while(it.hasNext())
{
ret[i++]=it.next();
System.out.println(i);
}
return ret;
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
}
}
|
可以看出,服務端點的主要任務是調用EJB組件來完成業務邏輯的。
需要向讀者說明的是,為了和本系列第一篇文章中的客戶端框架兼容(客戶端使用的值對象是com.hellking.webservice.BookVO,而這里由WSDL2Java生成的值對象是com.hellking.webservice.servlet.BookVO)。我們需要做以下的改動:
把生成的這些代碼中的com.hellking.webservice.servlet.BookVO全部改為com.hellking.webservice.BookVO,然后在Apache axis服務配置文件中申明這個BeanMapping,具體的聲明方法在后面介紹。
接下來的工作是編譯服務端相關的文件:BookServletServiceSoapBindingImpl、BookServletServiceSoapBindingSkeleton、BookServiceInterfaceService、BookServiceInterface。
服務端點部署
啟動服務器,這個服務器可以是任何能夠運行Apache引擎Web服務器,當然最好是同時有EJB容器和EJB容器的服務器,如Webphere 、Weblogic、JBOSS,如果沒有EJB容器,還需要一個額外的EJB容器,并且需要更改BookServletServiceSoapBindingImpl中獲得上下文(InitialContext)的方法,如:
例程6 獲得上下文環境
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY, "xxxxx");
p.put(Context.URL_PKG_PREFIXES, "xxxx");
p.put(Context.PROVIDER_URL, "xxxx");
init=new javax.naming.InitialContext(p);
|
在控制臺中,轉到deploy.wsdd目錄下,執行以下的命令就可以完成部署:
java org.apache.axis.client.AdminClient deploy.wsdd
|
由于我們使用的是自己的序列化Bean對象,故要在%Web-Apps%/WEB-INF/ server-config.wsdd文件中做以下更改:
找到
在中間加入以下內容:
部署后您必須確保在%Web-Apps%/WEB-INF/classes目錄下有服務端相關的類(BookServletServiceSoapBindingImpl、BookServletServiceSoapBindingSkeleton等)。
在瀏覽器里輸入(這個地址您需要根據具體情況更改):
http://localhost:8080/axis/services/BookServletService?wsdl
來驗證Web服務是否已經部署成功,如果部署不成功,您可以先嘗試重新啟動服務器。
客戶端
如果服務端已經成功部署,下一步的工作就是編寫客戶端程序了。由于使用WSDL2Java已經生成了客戶端的框架,所以我們的任務將相對簡單了。
客戶端編程任務主要有以下幾個:
- 在BookServletServiceSoapBindingStub里注冊BeanMapping;
- 編寫客戶端業務代表,這里使用了JAXRPCDelegate;
- 更改以前的BookGUI的部分程序。
在BookServletServiceSoapBindingStub里注冊BeanMapping
由于在SOAP消息中使用了序列化的BookVO對象,故在BookServletServiceSoapBindingStub中要進行BeanMapping注冊。具體方法:
找到BookServletServiceSoapBindingStub中的getAllBooks,getTheBookDetail,getBookByCategory方法,在每個方法中的
java.lang.Object _resp = _call.invoke(new java.lang.Object[] {in0});
|
前加入以下代碼:
例程7 在BookServletServiceSoapBindingStub注冊BeanMapping
QName qn = new QName( "BookServletService", "BookServletService" );
_call.registerTypeMapping(com.hellking.webservice.BookVO.class, qn,
new org.apache.axis.encoding.ser.BeanSerializerFactory(com.hellking.webservice.BookVO.class, qn),
new org.apache.axis.encoding.ser.BeanDeserializerFactory(com.hellking.webservice.BookVO.class, qn));
|
注意這里的Qname要和server-config.wsdd中描述的名稱空間一致。在中server-config.wsdd,我們使用了以下的映射:
在編寫業務代表程序前,我們先來對Web服務做一個調用測試。在測試前您必須保證數據庫里已經有圖書信息。如果EJB和Web Application都部署好,您可以通過以下頁面來往數據庫里增加數據:
http://localhost:8080/axis/insert_data.jsp
測試代碼如下:
例程8 測試Web服務
package com.hellking.webservice.servlet;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
import com.hellking.webservice.ejb.*;
public class Client {
public static void main(String[] args) {
try {
BookServiceInterfaceServiceLocator locator=new BookServiceInterfaceServiceLocator();
BookServiceInterface myProxy=locator.getBookServletService();
Object[] c=myProxy.getAllBooks();
com.hellking.webservice.BookVO
book=(com.hellking.webservice.BookVO)c[0];
System.out.println(book.getName());
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
|
如果在控制臺里打印出某個圖書的名字,那么就驗證了客戶端和服務端的部署是正確的。在進行下面的工作前,請確保這個測試是成功的。
編寫客戶端業務代表
對于客戶端程序來說,業務代表直接和Web服務打交道,獲得Web服務返回的數據,并做對應的處理,然后把數據返回給GUI程序,GUI程序只負責數據顯示。業務代表的代碼如下:
例程9 JAXRPCDelegate業務代表
package com.hellking.webservice.servlet;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
import com.hellking.webservice.ejb.*;
import java.util.*;
public class JAXRPCDelegate implements com.hellking.webservice.BookBusiness
{
BookServiceInterfaceServiceLocator locator;
com.hellking.webservice.servlet.BookServiceInterface bookService;
public JAXRPCDelegate()
{
try
{
locator=new BookServiceInterfaceServiceLocator();
bookService=locator.getBookServletService();
}
catch(Exception e)
{
}
}
public Collection getBookByCategory(String category)
{
System.out.println("by_category");
Collection ret=new ArrayList();
try
{
Object[] books=bookService.getBookByCategory(category);
System.out.println(category);
int i=0;
while(true)
{
ret.add(books[i++]);
System.out.println(i);
}
}
catch(Exception e)
{
}
return ret;
}
public Collection getAllBooks()
{
Collection ret=new ArrayList();
try
{
Object[] books=bookService.getAllBooks();
int i=0;
while(true)
{
ret.add(books[i++]);
}
}
catch(Exception e)
{
}
return ret;
}
public com.hellking.webservice.BookVO getTheBookDetail(String name)
{
System.out.println("bookdetail");
com.hellking.webservice.BookVO ret=new com.hellking.webservice.BookVO();
try
{
ret=bookService.getTheBookDetail(name);
}
catch(Exception e)
{
}
return ret;
}
}
|
和第一篇文章介紹的JAXMDelegate一樣,JAXRPCDelegate 同樣實現了BookBusiness接口,BookBusiness接口是以前設計的接口,我們在這里進行重用,這樣的好處是BookClientGUI程序幾只要做很少的更改就可以運行。
更改以前的BookClientGUI的部分程序
在BookGUI構造方法里增加以下內容:
例程10 更改BookGUI程序
public BookClientGUI()
{
business=new JAXRPCDelegate();
…
}
|
好了,經過以上的奮戰,讓我們來看運行的結果吧。
java com.hellking.webservice.BookClientGUI
運行結果如圖4所示。

總結
通過以上的介紹,相信讀者對JAX-RPC Web服務開發已經有一個比較深刻的認識。總結一下,使用JAX-RPC開發Web服務時,主要有以下的工作:
- 服務端點定義;
- 服務描述;
- 服務端點實現;
- 服務端點部署;
下一步
本文已經介紹了把Servlet作為Web服務端點開發Web服務的全過程,下一篇將是把EJB作為Web服務端點來開發。
參考資料
Apache axis User's Guides: http://ws.apache.org/axis/ Sun jwsdp-1_1-tutorial, http://java.sun.com/webservices/downloads/webservicestutorial.htmlhttp://www.ibm.com/developerworks/cn/xml/index.htmlXML & Web services專區 JAX-RPC API http://java.sun.com/webservices Jwdp1.1 http://java.sun.com/webservices 下載 樣例代碼
關于作者 陳亞強:北京華園天一科技有限公司高級軟件工程師,擅長J2EE技術,曾參與多個J2EE項目的設計和開發,對Web服務有很大的興趣并且有一定的項目經驗。熱愛學習,喜歡新技術。即將由電子工業出版社出版的《J2EE企業應用開發》正在最終定稿階段,目前正從事J2EE方面的開發和J2EE Web服務方面的圖書寫作。您可以通過 cyqcims@mail.tsinghua.edu.cn和他聯系。 |