Web Service,即“Web 服務(wù)”,簡(jiǎn)寫(xiě)為 WS,從字面上理解,它其實(shí)就是“基于 Web 的服務(wù)”。而服務(wù)卻是雙方的,有服務(wù)需求方,就有服務(wù)提供方。服務(wù)提供方對(duì)外發(fā)布服務(wù),服務(wù)需求方調(diào)用服務(wù)提供方所發(fā)布的服務(wù)。其實(shí)也就是這些了,沒(méi)有多少高大上的東西。
本文將從實(shí)戰(zhàn)的角度,描述使用 Java 開(kāi)發(fā) WS 的工具及其使用過(guò)程。
如果說(shuō)得再專(zhuān)業(yè)一點(diǎn),WS 其實(shí)就是建立在 HTTP 協(xié)議上實(shí)現(xiàn)異構(gòu)系統(tǒng)通訊的工具。沒(méi)錯(cuò)!WS 說(shuō)白了還是基于 HTTP 協(xié)議的,也就是說(shuō),數(shù)據(jù)是通過(guò) HTTP 進(jìn)行傳輸?shù)摹?/p>
自從有了 WS,異構(gòu)系統(tǒng)之間的通訊不再是遙不可及的夢(mèng)想。比如:可在 PHP 系統(tǒng)中調(diào)用 Java 系統(tǒng)對(duì)外發(fā)布的 WS,獲取 Java 系統(tǒng)中的數(shù)據(jù),或者把數(shù)據(jù)推送到 Java 系統(tǒng)中。
如果您想了解更多關(guān)于 WS 的那些概念與術(shù)語(yǔ),可以看看下面的百度百科:
http://baike.baidu.com/view/67105.htm
今天我想與大家分享的主題是,如何在 Java 中發(fā)布與調(diào)用 WS?希望本文能夠?qū)δ兴鶐椭?/p>
第一步:您要做的第一件事情就是,寫(xiě)一個(gè)服務(wù)接口。
package demo.ws.soap_jdk;import javax.jws.WebService;@WebServicepublic interface HelloService { String say(String name);}
在接口上放一個(gè) WebService
注解,說(shuō)明該接口是一個(gè) WS 接口(稱(chēng)為“Endpoint,端點(diǎn)”),其中的方法是 WS 方法(稱(chēng)為“Operation,操作”)。
第二步:實(shí)現(xiàn)這個(gè) WS 接口,在實(shí)現(xiàn)類(lèi)中完成具體業(yè)務(wù)邏輯,為了簡(jiǎn)單,我們還是寫(xiě)一個(gè) Hello World 意思一下吧。
package demo.ws.soap_jdk;import javax.jws.WebService;@WebService( serviceName = 'HelloService', portName = 'HelloServicePort', endpointInterface = 'demo.ws.soap_jdk.HelloService')public class HelloServiceImpl implements HelloService { public String say(String name) { return 'hello ' + name; }}
第三步:寫(xiě)一個(gè) Server 類(lèi),用于發(fā)布 WS,直接使用 JDK 提供的工具即可實(shí)現(xiàn)。
package demo.ws.soap_jdk;import javax.xml.ws.Endpoint;public class Server { public static void main(String[] args) { String address = 'http://localhost:8080/ws/soap/hello'; HelloService helloService = new HelloServiceImpl(); Endpoint.publish(address, helloService); System.out.println('ws is published'); }}
只需使用 JDK 提供的 javax.xml.ws.Endpoint
即可發(fā)布 WS,只需提供一個(gè) WS 的地址(address),還需提供一個(gè)服務(wù)實(shí)例(helloService)。
現(xiàn)在您就可以運(yùn)行 Server 類(lèi)的 main 方法了,會(huì)在控制臺(tái)里看到“ws is published”的提示,此時(shí)恭喜您,WS 已成功發(fā)布了!
第四步:打開(kāi)您的瀏覽器,在地址欄中輸入以下地址:
http://localhost:8080/ws/soap/hello?wsdl
注意:以上地址后面有一個(gè) ?wsdl
后綴,在 Server
類(lèi)中的 address 里卻沒(méi)有這個(gè)后綴。此時(shí),在瀏覽器中會(huì)看到如下 XML 文檔:
當(dāng)看到這份 WSDL 文檔時(shí),也就意味著,您發(fā)布的 WS 服務(wù)現(xiàn)在可以被別人使用了。
第一步:使用 JDK 提供的命令行工具生成 WS 客戶(hù)端 jar 包。
JDK 安裝目錄下有個(gè) bin 目錄,里面存放了大量的命令行工具,只要您的 Path
環(huán)境變量指向了該路徑,就能在命令控制臺(tái)上使用 JDK 提供的相關(guān)命令。
其中,有一個(gè)名為 wsimport
的命令行工具,正是用來(lái)通過(guò) WSDL 生成 WS 客戶(hù)端代碼的,您只需要輸入以下命令即可:
wsimport http://localhost:8080/ws/soap/hello?wsdljar -cf client.jar .rmdir /s/q demo
對(duì)以上三行命令解釋如下:
最終您將會(huì)得到一份名為 client.jar
的 jar 包,將這個(gè) jar 包配置到您的 classpath 中,方便在下面的代碼中使用其中的類(lèi)。
技巧:可以將以上三行命令放入一個(gè) bat 文件中,在 Windows 中雙擊即可運(yùn)行。
第二步:寫(xiě)一個(gè) Client 類(lèi),用于調(diào)用 WS,需要使用上一步生成的 WS 客戶(hù)端 jar 包。
package demo.ws.soap_jdk;public class Client { public static void main(String[] args) { HelloService_Service service = new HelloService_Service(); HelloService helloService = service.getHelloServicePort(); String result = helloService.say('world'); System.out.println(result); }}
以上這段代碼稍微有點(diǎn)怪異,其中 HelloService_Service
是 jar 包中類(lèi),可以將其理解為 WS 的工廠(chǎng)類(lèi),通過(guò)它可以生成具體的 WS 接口,比如,調(diào)用 service.getHelloServicePort()
方法,就獲取了一個(gè) HelloService
實(shí)例,正是通過(guò)這個(gè)實(shí)例來(lái)調(diào)用其中的方法。
運(yùn)行 Client 類(lèi)的 main 方法,就會(huì)看到您所期望的結(jié)果“hello world”了,不妨親自嘗試一下吧。
可見(jiàn),這是一個(gè)典型的“代理模式”應(yīng)用場(chǎng)景,您實(shí)際是面向代理對(duì)象來(lái)調(diào)用 WS 的,并且這是一種“靜態(tài)代理”,下面我們來(lái)談?wù)劊绾问褂谩皠?dòng)態(tài)代理”的方式來(lái)調(diào)用 WS?
其實(shí) JDK 已經(jīng)具備了動(dòng)態(tài)代理的功能,對(duì)于 WS 而言,JDK 同樣也提供了很好的工具,就像下面這段代碼那樣:
package demo.ws.soap_jdk;import java.net.URL;import javax.xml.namespace.QName;import javax.xml.ws.Service;public class DynamicClient { public static void main(String[] args) { try { URL wsdl = new URL('http://localhost:8080/ws/soap/hello?wsdl'); QName serviceName = new QName('http://soap_jdk.ws.demo/', 'HelloService'); QName portName = new QName('http://soap_jdk.ws.demo/', 'HelloServicePort'); Service service = Service.create(wsdl, serviceName); HelloService helloService = service.getPort(portName, HelloService.class); String result = helloService.say('world'); System.out.println(result); } catch (Exception e) { e.printStackTrace(); } }}
此時(shí),只需在本地提供一個(gè) HelloService 的接口,無(wú)需 client.jar,直接面向 WSDL 編程,只不過(guò)您需要分別定義出 serviceName
與 portName
這兩個(gè)東西,最后才能調(diào)用 JDK 提供的 javax.xml.ws.Service
類(lèi)生成 service 對(duì)象,它同樣是一個(gè)工廠(chǎng)對(duì)象,通過(guò)該工廠(chǎng)對(duì)象獲取我們需要的 HelloService
實(shí)例。貌似這種方式也不是特別動(dòng)態(tài),畢竟 HelloService 接口還是需要自行提供的。
通過(guò)本文,您可以了解到,不僅可以使用 JDK 發(fā)布 WS,也可以使用 JDK 調(diào)用 WS,這一切都是那么的簡(jiǎn)單而自然。但需要注意的是,這個(gè)特性是從 JDK 6 才開(kāi)始提供的,如果您還在使用 JDK 5 或更低的版本,那就很遺憾了,您不得不使用以下工具來(lái)發(fā)布與調(diào)用 WS,它們分別是:
當(dāng)然,發(fā)布與調(diào)用 WS 的工具不僅僅只有以上這些,而是它們是 Java 世界中最優(yōu)秀的 WS 開(kāi)源項(xiàng)目。
本文講述的 WS 其實(shí)是一種 Java 規(guī)范,名為 JAX-WS
(JSR-224),全稱(chēng) Java API for XML-Based Web Services,可以將規(guī)范理解為官方定義的一系列接口。
JAX-WS 有一個(gè)官方實(shí)現(xiàn),就是上面提到的 JAX-WS RI,它是 Oracle 公司提供的實(shí)現(xiàn),而 Apache 旗下的 Axis 與 CXF 也同樣實(shí)現(xiàn)了該規(guī)范。Axis 相對(duì)而言更加老牌一些,而 CXF 的前世就是 XFire,它是一款著名的 WS 框架,擅長(zhǎng)與 Spring 集成。
從本質(zhì)上講,JAX-WS 是基于 SOAP 的,而 SOAP 的全稱(chēng)是 Simple Object Access Protocol(簡(jiǎn)單對(duì)象訪(fǎng)問(wèn)協(xié)議),雖然名稱(chēng)里帶有“簡(jiǎn)單”二字,其實(shí)并不簡(jiǎn)單,不相信您可以百度一下。
為了讓 WS 的開(kāi)發(fā)與使用變得更加簡(jiǎn)單、更加輕量級(jí),于是出現(xiàn)了另一種風(fēng)格的 WS,名為 JAX-RS
(JSR-339),全稱(chēng) Java API for RESTful Web Services,同樣也是一種規(guī)范,同樣也有若干實(shí)現(xiàn),它們分別是:
其中,Jersey 是 Oracle 官方提供的實(shí)現(xiàn),Restlet 是最老牌的實(shí)現(xiàn),RESTEasy 是 JBoss 公司提供的實(shí)現(xiàn),CXF 是 Apache 提供的實(shí)現(xiàn)(上文已做介紹)。
可見(jiàn),CXF 不僅用于開(kāi)發(fā)基于 SOAP 的 WS,同樣也適用于開(kāi)發(fā)基于 REST 的 WS,這么好的框架我們?cè)跄苠e(cuò)過(guò)?
如何使用 CXF 簡(jiǎn)化我們的 WS 開(kāi)發(fā)?我們下期再見(jiàn)!
系列博文:
Web Service 那點(diǎn)事兒(2) —— 使用 CXF 開(kāi)發(fā) SOAP 服務(wù)Web Service 那點(diǎn)事兒(3) —— SOAP 及其安全控制
聯(lián)系客服