微軟公司提出.Net概念后很快就被認(rèn)為是以后網(wǎng)絡(luò)服務(wù)的發(fā)展方向,其中的Web service概念更被認(rèn)為是將來的工業(yè)標(biāo)準(zhǔn)(其中的SOAP、WSDL、UDDI已經(jīng)是標(biāo)準(zhǔn)了)。而作為微軟公司的老對手SUN公司自然不甘心落后,不久就開發(fā)了SUN-ONE(Sun[tm] Open Net Environment)用來開發(fā)Web Services,并提供了Java WSDP(Java Web Services Developer Pack)工具包。
Web Services簡單的來說就是網(wǎng)絡(luò)服務(wù),譬如一個商業(yè)客戶在網(wǎng)絡(luò)上通過SOAP協(xié)議發(fā)送一個請求,服務(wù)端接收這個請求并處理它,然后發(fā)送一個響應(yīng)給客戶端。Web Service程序在服務(wù)端需要用容器配置,這個容器可以是一個Servlet容器,如:Tomcat或者是基于EJB之上的J2EE 容器。Web Service還用WSDL(Web Service Description Language)描述自己,這種描述包括Web Service的名字、可以被調(diào)用的方法名、這些方法的參數(shù)以及發(fā)送請求的位置等,這樣用戶就可以方便地通過WSDL去發(fā)現(xiàn)Web Service并取得服務(wù),然后利用Java API for XML-based RPC(JAX-RPC)實(shí)現(xiàn)對Web Service的調(diào)用。
舉一個典型的例子,如有個商家想訂購商品,他可以直接到去每一家供貨商詢問,但有一個方便的方法是通過UDDI(Universal Description,Discovery and Integration)注冊中心去查找供貨商,發(fā)現(xiàn)哪些才是自己要找的供貨商。因?yàn)楣┴浬淘谧灾行淖运麄兊腤eb Service,這樣就能被網(wǎng)絡(luò)潛在的客戶所發(fā)現(xiàn)。
JAX-RPC中有一個工具:xrpcc,它可以根據(jù)WSDL產(chǎn)生一個stub類作為客戶端的代理,以及一個tie類作為服務(wù)端的代理。JAX-RPC將客戶端調(diào)用轉(zhuǎn)換成一個基于HTTP請求的SOAP消息發(fā)送給服務(wù)器,服務(wù)器收到客戶請求,把SOAP消息轉(zhuǎn)換成方法調(diào)用并調(diào)用服務(wù)器的Web Service方法,再通過JAX-RPC將結(jié)果包裝成SOAP消息形式返回給客戶。
用JAX-RPC開發(fā)Web Service非常的輕易。一個Web Service有兩個文件:一個是接口,用來定義Web Service的遠(yuǎn)端可以調(diào)用的方法;另外一個實(shí)現(xiàn)了這個接口方法的類。
如定義了Web Service方法的接口:
package coffees;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface CoffeeOrderIF extends Remote {
public Coffee [] getPriceList() //Web Service方法
throws RemoteException;
public String orderCoffee(String coffeeName, int quantity) //Web Service方法
throws RemoteException;
}
實(shí)現(xiàn)了上述接口的類如:
package coffees;
public class CoffeeOrderImpl implements CoffeeOrderIF {
public Coffee [] getPriceList() throws RemoteException; {
//方法內(nèi)容
}
public String orderCoffee(String coffeeName, int quantity)
throws RemoteException; {
//方法內(nèi)容
}
定義了Web Service,客戶就可以進(jìn)行遠(yuǎn)端調(diào)用了。一個典型的客戶調(diào)用程序如下:
package coffees;
public class CoffeeClient {
public static void main(String[] args) {
try {
CoffeeOrderIF coffeeOrder = new
CoffeeOrderServiceImpl().getCoffeeOrderIF();//調(diào)用服務(wù)端的Web Service方法
Coffee [] priceList =
coffeeOrder.getPriceList()://調(diào)用服務(wù)端的Web Service方法
for (int i = 0; i < priceList.length; i++) {
System.out.print(priceList[i].getName() + " ");
System.out.println(priceList[i].getPrice());
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
JAX-RPC可以創(chuàng)建基于RPC(remote procedure calls)和XML的“客戶-服務(wù)”程序。因?yàn)槭褂玫氖欠植嫉?#8220;客戶-服務(wù)”模式,RPC機(jī)制能使客戶運(yùn)行其它系統(tǒng)的程序。
一個遠(yuǎn)端調(diào)用是靠基于XML技術(shù)之上的SOAP協(xié)議描述與傳輸?shù)?。SOAP協(xié)議定義消息封裝結(jié)構(gòu),編碼規(guī)則和一個描述響應(yīng)的協(xié)定。這些調(diào)用和響應(yīng)是靠SOAP傳輸?shù)?,目前JAX-RPC是基于SOAP1.1和HTTP1.1。
盡管JAX-RPC是依靠復(fù)雜的協(xié)議(SOAP),但JAX-RPC的API對開發(fā)者隱藏了復(fù)雜性。在服務(wù)端,開發(fā)者指定接口中可以被遠(yuǎn)端調(diào)用的方法,開發(fā)者可以寫一個或多個類去實(shí)現(xiàn)這些方法。而客戶端程序非常輕易去編寫。通過指定服務(wù)端的URI,客戶可以簡單的調(diào)用這些方法。
使用JAX-RPC,客戶和服務(wù)器有一個非常大的優(yōu)勢:平臺獨(dú)立性。并且JAX-RPC不受限制,如:一個JAX-RPC客戶能夠調(diào)用不同平臺的Web Service,反之亦然。
因?yàn)镴AX-RPC是使用W3C(World Wide Web Consortium)技術(shù),所以具有擴(kuò)展性。
W3C包括:HTTP、SOAP和WSDL。
下面有一個簡單的例子說明JAX-RPC的實(shí)現(xiàn):
1. 調(diào)用一個遠(yuǎn)端程序:HelloClient程序調(diào)用本地stub對象中描述遠(yuǎn)端Web Service的方法;
2. stub對象通過JAX-RPC系統(tǒng)調(diào)用程序;
3. 系統(tǒng)將遠(yuǎn)端程序調(diào)用轉(zhuǎn)換成SOAP消息并通過HTTP把它傳輸?shù)椒?wù)器去;
4. 當(dāng)服務(wù)器接收到客戶的SOAP消息,通過JAX-RPC系統(tǒng)將SOAP消息轉(zhuǎn)換成方法調(diào)用;
5. 調(diào)用tie對象中的方法;
6. tie調(diào)用實(shí)現(xiàn)了HelloWorld服務(wù)的方法;
7. 系統(tǒng)將調(diào)用方法的結(jié)果轉(zhuǎn)換成SOAP消息響應(yīng)給客戶;
8. 客戶接收響應(yīng)來的SOAP消息,分析它并取得方法調(diào)用的結(jié)果。
服務(wù)端程序如下:
定義Web方法的接口HelloIF.java:
package hello;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface HelloIF extends Remote {
public String sayHello(String s) throws RemoteException; // Web 方法,可以被客戶調(diào)用
實(shí)現(xiàn)Web方法的類HelloImpl.java:
package hello;
public class HelloImpl implements HelloIF {
public String message ="Hello";
public String sayHello(String s) { //具體方法實(shí)現(xiàn)
return message + s;
}
}
客戶程序HelloClient.java:
package hello;
import javax.xml.rpc.Stub;
public class HelloClient {
public static void main(String[] args) {
try {
Stub stub = createProxy();//實(shí)現(xiàn)遠(yuǎn)端調(diào)用的對象
stub._setProperty(
javax.xml.rpc.Stub.ENDPOINT_ADDRESS_PROPERTY,
args[0]);
HelloIF hello = (HelloIF)stub;
System.out.println(hello.sayHello("Duke!"));//調(diào)用服務(wù)端的方法
} catch (Exception ex) {
ex.printStackTrace();
}
}
private static Stub createProxy() {
// Note: HelloWorld_Impl is implementation-specific.
return (Stub)(new HelloWorld_Impl().getHelloIFPort());
}
}
當(dāng)配置好服務(wù)端并編譯了客戶端HelloClient.java,可以運(yùn)行客戶程序如:ant run ,假如一切正確,將顯示:
Hello Duke!