本文根據 SUN 官方網站 Enterprise Java Technologies Tech Tips 欄目上的一篇文章改寫的,所有過程均調試通過。
一.前言
SOA 思想的核心在于 "S" ,凡是從服務的角度去看待系統功能,并且構建和實現應用,都可以認為是 SOA 的某種實現形式。下面一段要說的是: SOA 技術的核心在于 "O" --只有做到 Service-Orientation 的技術,才能真正稱為 SOA 技術。
怎樣才能算作 Service-Orientation ?我們再以 OO 進行類比: VB 之所以被稱為基于對象 (Object-Based) 而不是面向對象 (Object-Oriented) 的語言,是因為 VB 的運行時結構不具有 VMT 等基本構造,語法上也不支持私有成員,繼承等基礎特性,兩者結合,就造成無法支持封裝,繼承,多態等面向對象的關鍵技術。一句話, VB 不是圍繞 “ 對象 ” 這個核心概念設計的語言。 對于 SOA 技術,它也可以從這個角度劃分兩個層次:一個是 SOA 的 “ 運行時 ” 相關結構標準,例如 SOAP , WSDL,WS-* 等,相當于 OO 中的對象內部結構, VMT 構造等。另外一個則是語言和工具層面的支持,例如基于元數據的服務描述,支持 Web Service 的類庫, Proxy 生成工具等,相當于 OO 語言中提供的基本 OO 語法,類庫,編繹器等。這兩個層次共同構成 SOA 體系結構中的要素,讓人們能夠圍繞 “ 服務 ” 這個核心概念進行系統開發和應用。因此,一種技術架構,平臺或產品要稱作 SOA ,應當同時具備這一兩方面的特征。
所以,下面將要討論的 JAX-WS 技術,從上面的觀點來看,也就是 SOA 技術的第二個層次,即語言和工具層面的支持 --Java ,例如基于元數據的服務描述,支持 Web Service 的類庫, Proxy 生成工具等 .
Java API for XML Web Services ( JAX-WS ) 2.0, 是 Java EE 5 平臺的一個重要的部分。作為 Java API for XML-baseed RPC 1.1 ( JAX-RPC )的后續版本,在 JAX-RPC 1.1 中 , 開發人員需要寫一個接口類 Service Endpoint Interface(SEI), 在 JAX-WS 2.0 中 , 開發人員一上來就可以直接寫自己的實現類 . 通過使用 annotations, 自動生成 SEI 和其他一些文件 . 這樣有助于開發人員專注于自己想開發的部分 , 而不必要地分散精力去維護其他的一些附屬文件 .
二 . 快速實踐 JAX-WS2.0
下面通過一個兩個數相加的簡單例子來看看 ,JAX-WS 2.0 API 的應用過程 . 程序是一個獨立的客戶端傳給服務端兩個整數 , 經過服務端處理后 , 將結果返回到客戶端并打印出來 .
一 . 環境配置 .
1. JDK 5.0 or higher
下載 : http://java.sun.com/javase/downloads/index.jsp
2. Java EE 5.0 App Server.
這個例子是基于 Java EE 5 的一個開源實現項目 GlassFish 。 https://glassfish.dev.java.net/public/downloadsindex.html
本例子所需的基本代碼的壓縮包可以通過這個 鏈接 下載。里面包括了這個例子需要的代碼,構建腳本和一個 build 文件。
環境變量的配置:
·
GLASSFISH_HOME.
這個應該指向你安裝
GlassFish
的目錄
(
比如,我系統上的是:
J:/Sun/AppServer)
·
ANT_HOME.
這個應該指向
ant
所安裝的目錄。在你下載
GlassFish bundle
時
Ant
已經被包含在里面了。
(
對于
Windows
系統,它是在
lib/ant
子目錄
)
。不過你也可以從
Apache Ant Project page.
下載
Ant
。對于這個例子需要
Apache ant 1.6.5
·
JAVA_HOME.
這個應該指向你系統上安裝的
JDK 5.0
(
or higher
)的目錄。
同時,把
ant
的
bin
目錄添加到
Path
環境變量中去
(J:/apache-ant-1.6.5/bin)
,當然了
JDK
的
bin
目錄也加進去了。
然后
下載例子的代碼包
并且解壓。根文件夾是
jaxws-techtip
。
endpoint/
目錄下有一個文件
Calculator.java
client/
目錄下有一個文件
JAXWSClient.java
二
.
編寫構建服務端
隨著第一步環境配置的完全,現在該開始構建一個 web 服務了。在這個例子里, web 服務是從一個 Java 類來開發的。為了構建這個 web 服務:
1 . 寫一個端點實現類 ( endpoint implementation class) 。
2 . 編繹這個 端點實現類。
3 . 有選擇的產生對 web 服務的運行必須具備的那些可移植的制品。
4 . 把 web 服務打包成一個 WAR 文件并且在 App Server 中部署它。
1 編寫實現類 .
進到
endpoint/
目錄下,可以看到里面有一個文件
Calculator.java.
它是一個端點實現類,具備有對兩個整數進行相加的簡單服務。
JAX-WS 2.0
大量地依賴注釋(
annotations
)的使用,它是
A Metadata Facility for the Java Programming Language (JSR 175)
描述的規范和
Web Services Metadata for the Java Platform (JSR 181)
描述的規范。






















研究上面的實現類
Calculator
,注意到類里的兩個注釋的使用
@WebService
跟
@WebMethod
。一個正確的端點實現類必須包含有一個
@WebService
注釋。這個注釋標注這個類將作為一個
web
服務對外開放。
@WebService
的
name
屬性表明了
web
服務描述語言
(WSDL)
里的端口類型
(
portType
)
(在這個例子里是
”Calculator”
)。而
serviceName="CalculatorService"
對應的是一個
WSDL
里的服務元素
(service)
。
targetNamespace
屬性為
WSDL
說明了
XML
的命名空間。所有的這些屬性都是可選的。對于這些屬性的默認值是什么,請參考
Web Services Metadata for the Java Platform
規范,
JSR 181
。
再來看看另外一個重要的注釋
@WebMethod
,被它注釋過的方法說明將它以一個
web
服務的方法暴露出來,被其他應用來調用。
@WebMethod
注釋里的
operationName
聲明了
WSDL
里的一個元素
WSDL
operation
(在這個例子里,
”add”
)
,
另外一
個屬性
action =("
urn:Add
"),
它為
WSDL
還有一些從這個
web
服務操作
(web service operation)
生
成的元素聲明了一個命名空間。這兩個屬性都是可選的。如果你沒有列出來的
話, WSDL 操作 (operation) 的值將會默認為方法名,還有 action 值也會默認為
服務的
targetNamespace
。
2
編繹實現類
寫完了上面的實現類之后,你需要編繹它。點擊
開始-
>
程序-
>Sun Microsystems
-
>“Start Default Server”
啟動應用服務器或者通過在
DOS
窗口下敲下面的命令來啟動它:
<GF_install_dir>/bin/asadmin start-domain domain1
,其中
GF_install_dir
是你安裝
GlassFish
的目錄,也就是說先到
<GF_install_dir>/bin
目錄下,然后用命令
asadmin start-domain domain1
來啟動應用服務器?,F在將目錄轉到
jaxws-techtip
文件夾下,運行下面的
ant
命令,也就是執行第一個任務
complie:
ant compile
執行這個命令就相當于執行以下的 javac 命令 ( 都是在同一行 ) :
javac -classpath $GLASSFISH_HOME/lib/javaee.jar -d
./build/classes/service/ endpoint/Calculator.java
3 為 web 服務的執行產生可移植的制品
這一步是可選的。如果在這個 web 服務的部署期間,他們沒有和一個可配置的服務單元綁定, GlassFish 的部署工具能夠自動地產生這些制品。然而對于剛剛接觸 JAX-WS 來說,對于弄清楚整個編程模式來說,通過手動產生地會話會更有幫助,即運行下面的命令:
ant generate-runtime-artifacts
這個任務將會在 jaxws-techtip 目錄下生成 build/generated 目錄,并且運行了下面的 wsgen 命令 ( 都是在同一行 ):
$GLASSFISH_HOME/bin/wsgen -cp ./build/classes/service -keep -d ./build/classes/service –r ./build/generated -wsdl endpoint.Calculator
一個 WSDL 文件 (CalculatorService.wsdl) 在 build/generated 目錄下生成了,還在同個目錄下生成了另外一個 schema 文件 (CalculatorService_schema1.xsd), 它為 CalculatorService.wsdl 定義了 schema 。
JavaBean 技術組件 ( JavaBeans ) 在編組 ( marshaling,java->XML ) 的方法調用,響應,還有 service-specific 異常 中起了很大的作用。這些類將會在 web 服務在一個應用服務器中運行的時候被使用。 JavaBean 類在 jaxws-techtip 目錄下的 /build/classes/service/endpoint/jaxws 目錄被生成了,這些類是:
Add.java
Add.class
AddResponse.java
AddResponse.class
4 打包并部署 WAR 文件
接下來你需要做的工作就是對服務進行打包和部署。為了做這個,你需要在一個部署描述符中詳細說明這個服務。 Web 服務可以綁定成 servlet 的形式或者無狀態的 session bean 形式打包成 Web Archive (WAR) 文件。在這個例子里把它綁定為一個 servlet 。
為了把這個服務打包成一個 WAR 文件,定位到 jaxws-techtip 文件夾,并且在 DOS 窗口上運行下面的命令:
ant pkg-war
對于這個 war 文件的結構,我們可以到 build.xml 文件里看看 pkg-war 目標:
<
target
name
="pkg-war"
depends
="init-common">
<
mkdir
dir
="${assemble.dir}"/>
<
echo
message
="mybuildclassesdiris:${build.classes.dir}"
level
="verbose"/>
<
mkdir
dir
="${build.classes.dir}/tmp"/>
<
mkdir
dir
="${build.classes.dir}/tmp/WEB-INF"/>
<
mkdir
dir
="${build.classes.dir}/tmp/WEB-INF/classes"/>
<
mkdir
dir
="${build.classes.dir}/tmp/WEB-INF/wsdl"/>
<
copy
file
="${web.xml}"
tofile
="${build.classes.dir}/tmp/WEB-INF/web.xml"
failonerror
="false"/>
<
copy
todir
="${build.classes.dir}/tmp/WEB-INF/classes">
<
fileset
dir
="${build.classes.dir}/service">
<
include
name
="**/*.class"/>
<
include
name
="**/${handler.name}"/>
</
fileset
>
</
copy
>
<
copy
todir
="${build.classes.dir}/tmp/WEB-INF/wsdl">
<
fileset
dir
="${build.generated.dir}">
<
include
name
="**/*.*"/>
</
fileset
>
</
copy
>
<
echo
message
="Creatingwarfile${assemble.dir}/${appname}-web.war"
level
="verbose"/>
<
jar
jarfile
="${assemble.dir}/${appname}-web.war"
update
="true">
<
fileset
dir
="${build.classes.dir}/tmp"
casesensitive
="yes">
<
include
name
="**/*class*"/>
<
include
name
="**/${handler.name}"/>
</
fileset
>
<
fileset
dir
="${build.classes.dir}/tmp/"
casesensitive
="true">
<
include
name
="WEB-INF/web.xml"/>
</
fileset
>
<
fileset
dir
="${build.classes.dir}/tmp"
casesensitive
="yes">
<
include
name
="WEB-INF/wsdl/*.*"/>
</
fileset
>
</
jar
>
<
echo
message
="createdwarfile${assemble.dir}/${appname}-web.war"
level
="verbose"/>
</
target
>
我們可以通過執行下面的命令來部署已經生成的 war 文件:
ant deploy-app
這等同于執行下面的 asadmin 部署命令 ( 都是在同一行 ) :
bash$GLASSFISH_HOME/bin/asadmin deploy --user admin
--passwordfile passwd --host localhost --port 4848
--contextroot jaxws-webservice --upload=true --target server
三 編寫構建客戶端
在你部署完這個 web 服務之后,你可以通過一個客戶端程序來訪問它。下面是構建這個客戶端的步驟:
1 編寫客戶端
2 生成編繹這個客戶端必須要有的可移植制品。
3 編繹客戶端。
4 運行客戶端。
5.1 編寫客戶端
下面的程序, JAXWSClient, 是一個獨立的客戶端程序,它在這個例子所提供的代碼里可以找到。這個客戶端類調用了部署好的服務的一個 add 操作十次,從數字 0 到 9 挨個加 10.
package
client;
import
javax.xml.ws.WebServiceRef;
import
com.techtip.jaxws.sample.CalculatorService;
import
com.techtip.jaxws.sample.Calculator;
public
class
JAXWSClient
{
@WebServiceRef(wsdlLocation=
"http://localhost:8080/jaxws-webservice/CalculatorService?WSDL")
static
CalculatorServiceservice;
public
static
void
main(String[]args)
{
try
{
JAXWSClientclient=
new
JAXWSClient();
client.doTest(args);
}
catch
(Exceptione)
{
e.printStackTrace();
}
}
public
void
doTest(String[]args)
{
try
{
System.out.println(
"Retrievingportfromtheservice"+service);
Calculatorport=service.getCalculatorPort();
System.out.println(
"Invokingaddoperationonthecalculatorport");
for
(
int
i=0;i>10;i++)
{
int
ret=port.add(i,10);
if
(ret!=(i+10))
{
System.out.println("Unexpectedgreeting"+ret);
return
;
}
System.out.println(
"Adding:"+i+"+10="+ret);
}
}
catch
(Exceptione)
{
e.printStackTrace();
}
}
}
研究下上面代碼的特點,在 JAXWSClient 類里的 @WebServiceRef 注釋是用來定義一個 web 服務的引用。 @WebServiceRef 注釋的 wsdlLocation 參數它指向了一個所要引用的服務的 WSDL 文件。 @WebServiceRef 注釋支持其它的可選屬性,就像在 JSR 224 里所說的。靜態變量名 service 將會被客戶端容器在運行時被動態地注入。
注意到 JAXWSClient 的 import 語句:
com.techtip.jaxws.sample.CalculatorService and com.techtip.jaxws.sample.Calculator .
這些 import 語句是對那些在下一步里將要產生的可移植制品的聲明。 CalculatorService 是服務實現類的可移植制品。 Calculator 是一個對于服務端點的 Java 接口,它是從 @WebServiceRef 注釋中的 wsdlLocation 屬性所說明的 WSDL 文件生成的。
這個客戶端從 getWebServiceRefNamePort 方法得到一個 CalculatorService ,從而得到一個端點 Calculator 接口 Calculator port = service.getCalculatorPort();WebServiceRefName 是 @WebServiceRef 注釋的 name 屬性,或者說是在生成的 WSDL 文件里 WSDP 端口的值。在獲得了這個端點后,客戶端調用了十次加的操作。
5.2 生成客戶端的可移植的制品
就像在之前所提到的, CalculatorService 跟 Calculator 都是可移植的制品。為了生成客戶端所需的所有制品,定位到 jaxws-techtip 文件夾,并且在 DOS 窗口下輸入下面的命令:
ant generate-client-artifacts
這相當于執行下面的 wsimport 命令 ( 都在同一行里 ) :
$GLASSFISH_HOME/bin/wsimport -keep-d ./build/classes/client
http://localhost:8080/jaxws-webservice/CalculatorService?WSDL
這將會在 jaxws-techtip 文件夾的 build/classes/client/com/techtip/jaxws/sample 目錄下生成以下的制品:
Add.java
Add.class
AddResponse.java
AddResponse.class
Calculator.java
Calculator.class
CalculatorService.java
CalculatorService.class
package-info.java
package-info.class
ObjectFactory.class
ObjectFactory.java
5.3 編繹客戶端類
下一步需要做的工作就是編繹客戶端類。我們可以通過輸入下面的命令來完成這項工作:
ant compile-client
ant 編繹任務將會編繹 client/JAXWSClient 并且把 class 文件寫到 build /classes/client 子目錄下。它等同于運行下面的命令 ( 都是在同一行 ) :
javac -d ./build/classes/client
-classpath $GLASSFISH_HOME/lib/javaee.jar:
$GLASSFISH_HOME/lib/appserv-ws.jar:
./build/classes/client client/JAXWSClient.java
5 . 4 運行客戶端
為了了解這個例子是如何工作的,運行下面的命令:
ant runtest-jaxws
它就相當于在 build/classes/client 文件夾下, 運行下面的命令:
$GLASSFISH_HOME/bin/appclient -mainclass client.JAXWSClient
在 DOS 窗口可以看到類似下面的輸出:
runtest-jaxws:
[echo] Executing appclient with client class as
client.JAXWSClient
[exec]Retrieving port from the service
com.techtip.jaxws.sample.CalculatorService@162522b
[exec]Invoking add operation on the calculator port
[exec]Adding : 0 + 10 = 10
[exec]Adding : 1 + 10 = 11
[exec]Adding : 2 + 10 = 12
[exec]Adding : 3 + 10 = 13
[exec]Adding : 4 + 10 = 14
[exec]Adding : 5 + 10 = 15
[exec]Adding : 6 + 10 = 16
[exec]Adding : 7 + 10 = 17
[exec]Adding : 8 + 10 = 18
[exec]Adding : 9 + 10 = 19
all:
BUILD SUCCESSFUL
Total time: 6 seconds
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
