本Guide利用Eclipse以及Ant建立一個簡單的Web Service,以示範Web Service的基本開發過程:
1.系統條件:
Eclipse Java EE IDE for Web Developers
Java SE 6
Windows XP
2.基本環境搭建:
1)Java SE6 JDK的安裝:下載下傳Java SE6 JDK,輕按兩下,安裝預設選項進行安裝即可。
2)Eclipse的安裝與配置:
安裝時直接解壓。
配置處有兩點,Window>Preferences>Java>Installed JREs確定如下設定:

安裝路徑可能略有不同。
Window>Preferences>Java>Compiler 確定如下設定:
3.建立Server端工程和相關包與類:
建立一個Java Project,命名為wsServerHelloWorld:
在這個項目下建立包:org.gnuhpc.wsServer
在這個包下邊建立類:SayHello
在SayHello.java檔案中輸入以下代碼:
package org.gnuhpc.wsServer; import javax.jws.WebService; @WebService public class SayHello { private static final String SALUTATION = "Hello"; public String getGreeting(String name) { return SALUTATION + " " + name; } }
其中注意到@WebService ,這個稱作annotation或者metadata,Java SE 5中的Web Services Metadata Specification引入的。Java SE 6中對于Web Services規範的更新以及JAX-WS(Java API
for XML Web Services)2.0規範,這些更新使得我們Web Services的建立和調用變得更加容易。使用這些新功能,我們可以僅僅使用簡單的Annotations注釋從一個Java類建立Web Services。開發者将其類和方法之前用該annotations指定,類告訴runtime engine以Web Service的方式和操作來使能該類和方法。這個annotations可以産生一個可布署的Web Service,是一個WSDL映射annotations,将Java源代碼與代表Web
Service的WSDL元素連接配接在了一起。
4.使用Ant産生Server端代碼:
首先在項目中建立一個檔案:build.xml,然後使用OpenWith>AntEditor打開,輸入以下腳本代碼:
<project default="wsgen">
<target name="wsgen">
<exec executable="wsgen">
<arg line="-cp
./bin -keep -s ./src -d ./bin
org.gnuhpc.wsServer.SayHello"/>
</exec>
</target>
</project>
<project default="wsgen"> <target name="wsgen"> <exec executable="wsgen"> <arg line="-cp ./bin -keep -s ./src -d ./bin org.gnuhpc.wsServer.SayHello"/> </exec> </target> </project>
default指定了預設執行的Target為wsgen,wsgen可以建立一個能夠使用WebService的類,它生成所有用于WebService釋出的源代碼檔案和經過編譯過的二進制類檔案。它還生成WSDL和符合規範的該類的WebService。
Target名稱為wsgen,具體執行的指令的參數:
-cp 為類路徑
-keep後産生的java檔案
-s 産生的源檔案放在哪
-d 産生的輸出問價放在哪
然後使用Ant Build選項運作:
在成功執行的提示後,我們重新整理一下Project
我們在Project區域可以看到,結果如下:
5.分布Web Service
org.gnuhpc.wsServer下建立一個類RunService:
import javax.xml.ws.Endpoint; public class RunService { /** *@paramargs */ public static void main(String[] args) { System.out.println("SayHello Web Service started."); new SayHello());
運作Run As>Java Application。我們得到結果,說明這個web service的Server端已經啟動。
6.檢視WSDL:
你可以看看到底WSDL都記錄了哪些資訊。看完後可以停止該Server。
7.監測Server
我們建立完Server可以用過Eclipse Web Services Explorer監測Server,
Window>Open Perspective>Other >JavaEE
打開Eclipse Web Services Explorer
點選右上角的WSDL Page按鈕:
我們可以觸發一個Web Service操作:點選getGreetings,添加一個參數,比如gnuhpc,然後點選Go按鈕:
8.建立Client端 工程和相關包與類:
建立一個Java Project,命名為wsClientHelloWorld,在這個項目下建立包:org.gnuhpc.wsClient
9.使用Ant産生Client代碼架構:
編寫Web service時,可以使用工具來利用WSDL生成進行調用的用戶端樁;或者也可以使用底層API來手動編寫Web service。前者友善,後者靈活,現在我們通過前者做說明:
建立檔案build.xml
New>File>build.xml
<project default="wsimport"> <target name="wsimport"> <exec executable="wsimport"> <arg line="-keep -s ./src -p org.gnuhpc.wsClient -d ./bin http://localhost:8080/wsServerExample?wsdl"/> </project> <project default="wsimport"> <target name="wsimport"> <exec executable="wsimport"> <arg line="-keep -s ./src -p org.gnuhpc.wsClient -d ./bin http://localhost:8080/wsServerExample?wsdl"/> </exec> </target> </project>
注意:wsgen 支援從 Java class 建立 Web services,wsimport 支援從 WSDL 建立 Web services,分别對應于 JAX-RPC 方式下的 Java2WSDL 和 WSDL2Java。要根據釋出的WSDL進行建立,這也就是為什麼要先運作RunServer的原因了。
運作Server的RunService : Run As>Java Application>
運作該Ant腳本,産生Client代碼:Run As>Ant Build
運作成功的提示如下:
生成的代碼如下:
這一步讀取WSDL并生成用戶端樁。這些樁是将為我們的代碼所用的Java類和接口。這些樁給伺服器端功能提供了一個用戶端接口。例如,如果我們的伺服器提供一個Maths服務,該服務帶有一個叫做add的方法。我們的用戶端代碼将調用樁上的一個方法,而樁實作将對該方法使用參數封裝,把Java方法調用變為Web service請求。這個請求将基于HTTP發送給伺服器,而且将使用SOAP作為RPC協定。監聽伺服器接收該SOAP消息,然後(十有八九)将其轉換為伺服器處的一次方法調用。
10.編寫Client代碼
建立一個類:SayHelloClient
package org.gnuhpc.wsClient; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import javax.xml.ws.BindingProvider; public class SayHelloClient { * @param args SayHelloService shs = new SayHelloService(); SayHello sh = (SayHello) shs.getSayHelloPort(); ((BindingProvider) sh).getRequestContext().put( BindingProvider.ENDPOINT_ADDRESS_PROPERTY, System.out.println(((BindingProvider) sh).toString()); String userName = null; boolean exit = false; while (!exit) { System.out.print("/n Please enter yourname (type 'quit' to exit): "); BufferedReader br = new BufferedReader(new InputStreamReader( System.in)); try { userName = br.readLine(); } catch (IOException e) { System.out.println("Errorreadingname."); System.exit(1); } if (!(exit = userName.trim().equalsIgnoreCase("quit") || userName.trim().equalsIgnoreCase("exit"))) { System.out.println(sh.getGreeting(userName)); } System.out.println("/nThank you for running the client.");
11.運作Client
右鍵SayHelloClient.java,選擇Run As> Java Application,得到:
可以使用腳本完成對Server和Client的調用:
在Client中建立檔案buildall.xml:
<project default="runClient"> <!-- ================================= target: wsimport ================================= --> <target name="wsimport" description="--> Read the WSDL and generate the required artifacts"> <exec executable="wsimport"> <arg line="-keep -s ./src -p org.gnuhpc.wsClient -d ./bin http://localhost:8080/wsServerExample?wsdl"/> </exec> </target> target: runServer <target name="runServer" description="--> Runs the Web service server from a terminal"> <echo> Running the following command from the terminal to run the server: ${java.home}/bin/java -cp "C:/Documents and Settings/Administrator/workspace/wsServerHelloWorld/bin" org.gnuhpc.wsServer.RunService </echo> <exec dir="c:/Progra~1/Java/jdk1.6.0_13/bin" executable="cmd" spawn="true" os="Windows XP" description="runs on XP"> <arg line="start cmd /K start cmd /K" /> <arg line='"c:/Progra~1/Java/jdk1.6.0_13/bin/java" -cp "C:/Documents and Settings/Administrator/workspace/wsServerHelloWorld/bin" org.gnuhpc.wsServer.RunService' /> target: pause <target name="pause" depends="runServer" description="--> Pauses briefly while the server starts"> <sleep seconds="5"/> </target> <!-- ================================= target: runClient ================================= --> <target name="runClient" depends="pause" description="--> Runs a Web service client from a terminal"> <echo> Running the following command from the terminal to run the client: ${java.home}/bin/java -cp "c:/DOCUME~1/Administrator/workspace/wsClientHelloWorld/bin" org.gnuhpc.wsClient.SayHelloClient </echo> <exec dir="c:/Progra~1/Java/jdk1.6.0_13/bin/" executable="cmd" spawn="true" os="Windows XP" description="Runs <arg line="start <arg line='"c:/Progra~1/Java/jdk1.6.0_13/bin/java -cp" "c:/DOCUME~1/Administrator/workspace/wsClientHelloWorld/bin" org.gnuhpc.wsClient.SayHelloClient' /> </exec> <project default="runClient"> <!-- ================================= target: wsimport ================================= --> <target name="wsimport" description="--> Read the WSDL and generate the required artifacts"> <exec executable="wsimport"> <arg line="-keep -s ./src -p org.gnuhpc.wsClient -d ./bin http://localhost:8080/wsServerExample?wsdl"/> </exec> </target> <!-- ================================= target: runServer ================================= --> <target name="runServer" description="--> Runs the Web service server from a terminal"> <echo> Running the following command from the terminal to run the server: ${java.home}/bin/java -cp "C:/Documents and Settings/Administrator/workspace/wsServerHelloWorld/bin" org.gnuhpc.wsServer.RunService </echo> <exec dir="c:/Progra~1/Java/jdk1.6.0_13/bin" executable="cmd" spawn="true" os="Windows XP" description="runs on XP"> <arg line="start cmd /K start cmd /K" /> <arg line='"c:/Progra~1/Java/jdk1.6.0_13/bin/java" -cp "C:/Documents and Settings/Administrator/workspace/wsServerHelloWorld/bin" org.gnuhpc.wsServer.RunService' /> </exec> </target> <!-- ================================= target: pause ================================= --> <target name="pause" depends="runServer" description="--> Pauses briefly while the server starts"> <sleep seconds="5"/> </target> <!-- ================================= target: runClient ================================= --> <target name="runClient" depends="pause" description="--> Runs a Web service client from a terminal"> <echo> Running the following command from the terminal to run the client: ${java.home}/bin/java -cp "c:/DOCUME~1/Administrator/workspace/wsClientHelloWorld/bin" org.gnuhpc.wsClient.SayHelloClient </echo> <exec dir="c:/Progra~1/Java/jdk1.6.0_13/bin/" executable="cmd" spawn="true" os="Windows XP" description="Runs on XP"> <arg line="start cmd /K start cmd /K" /> <arg line='"c:/Progra~1/Java/jdk1.6.0_13/bin/java -cp" "c:/DOCUME~1/Administrator/workspace/wsClientHelloWorld/bin" org.gnuhpc.wsClient.SayHelloClient' /> </exec> </target> </project>
注意其中的路徑名稱,選擇與你自己系統的路徑名即可。
在這個腳本中,預設target為runClient,但是在運作runClient之前還有一個依賴:pause,意味着runClient之前一定要運作pause,而pause的依賴是runServer,那麼運作順序就是
runServer先運作,pause再運作,最後runClient運作。
另一個需要注意的是os值:隻有目前系統與指定的OS比對時才會被執行。
為顯示指令。
用Ant Build運作得到一個Server,5秒鐘後出現一個Client。
12.使用SOAP螢幕監視C-S的通信:
到這一步,我們已經建立了一個Server 一個Client端,我們現在想使用Eclipse的TCP/IP Monitor監視SOAP通信。
打開:Window>Show View>Other>Debug>TCP/IP Monitor
配置:Windows>Preferences >Run/Debug > TCP/IPMonitor
添加一個TCP/IP Monitor:
Port為遠端伺服器端口,Local Monitoring Port為本地監聽端口
現在我們需要更新一下Client代碼(将端口8080,設定為8081),将Web Service通過Monitor重定向。然後運作腳本:
左右分别的文本全文為:
POST /wsServerExample HTTP/1.1 SOAPAction: "" Accept: text/xml, multipart/related, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 Content-Type: text/xml; charset=utf-8 User-Agent: Java/1.6.0_13 Host: localhost:8081 Connection: keep-alive Content-Length: 210 http://schemas.xmlsoap.org/soap/envelope/">http://wsServer.gnuhpc.org/">gnuhpc
HTTP/1.1 200 OK Transfer-encoding: chunked Content-type: text/xml; charset=utf-8 5d http://schemas.xmlsoap.org/soap/envelope/"> 8f >http://wsServer.gnuhpc.org/">Hello gnuhpc
其中的含義不言自明。
所有代碼下載下傳:
http://cid-a0a0b50959052db4.skydrive.live.com/self.aspx/.Public/WebService.rar
2010.1.5 補充:
2.JAX-WS 包括了 Java Architecture for XML Binding (JAXB) 和 SOAP with Attachments API for Java (SAAJ).
前者為從XML Schema到Java代碼表示提供了一個友善的映射方法,屏蔽了從SOAP消息中的XML Schema到Java代碼之間轉換的具體細節。而SAAJ則為處理在SOAP消息中附帶的XML提供了一個标準的方法。另外,JAX-WS還定義了從WSDL上定義的服務到實作這些服務的Java類之間的映射,任何定義在WSDL中的複雜類型都将根據JAXB定義的标準轉換到Java類中。
3.開發JAX-WS有兩種思路:
Contract first:先寫好WSDL,然後從中生成Java 代碼來實作。
Code first:先寫好一些plain old Java object (POJO) classes,然後使用annotations産生WSDL和Java類。
4.常見Annotations含義:
<a target="_blank">The <code>@WebService</code> annotation的含義:</a>
<a target="_blank">The <code>@WebService</code> annotation is defined by the <code>javax.jws.WebService</code> interface and it is placed on an interface or a class that is intended to be used as a service. <code>@WebService</code> has the following properties:</a>
<a target="_blank"></a>
<a target="_blank"> <code>@WebService</code> Properties</a>
Property
Description
<a target="_blank">name</a>
Specifies the name of the service interface. This property is mapped to the <code>name</code> attribute
of the <code>wsdl:portType</code> element that defines the service's interface in a WSDL contract.
<a target="_blank">targetNamespace</a>
Specifies the target namespace under which the service is defined. If this property is not specified, the target namespace is derived
from the package name.
<a target="_blank">serviceName</a>
Specifies the name of the published service. This property is mapped to the <code>name</code> attribute
of the <code>wsdl:service</code> element that defines the published service. The default is to use the name of the service's implementation
<a target="_blank">wsdlLocation</a>
Specifies the URI at which the service's WSDL contract is stored.
The default is the URI at which the service is deployed.
<a target="_blank">endpointInterface</a>
Specifies the full name of the SEI that the implementation class implements.
This property is only used when the attribute is used on a service implementation class.
<a target="_blank">portName</a>
Specifies the name of the endpoint at which the service is published. This property is mapped
to the <code>name</code> attribute of the <code>wsdl:port</code> element
that specifies the endpoint details for a published service. The default is the append <code>Port</code> to the name of the service's implementation
you generate WSDL from an SEI the interface's name is used in place of the implementation class' name
<code>@XmlSeeAlso:</code>
Suppose you want to build a web service that manages the inventory for a store that sells wakeboards and related equipment. Wakeboards are short boards made of buoyant material that are
used to ride over the surface of a body of water, typically behind a boat or with a cable-skiing apparatus.
For simplicity, let's assume that the store sells only three types items: wakeboards, bindings, and towers for boats. You want the web service to be
fairly simple to use and have a minimal amount of exposed operations. So to keep things simple, the web service uses an abstract <code>Item</code>class in its operations
instead of using type-specific operations. The following <code>Item</code> class can be used to model any inventory object that you might want to expose through
your web service:
<code> public abstract class Item implements Serializable {</code>
<code> private long id;</code>
<code> private String brand;</code>
<code> private String name;</code>
<code> private double price; </code>
<code> ... </code>
<code> }</code>
Extending the <code>Item</code> class, you can define
the following <code>Wakeboard</code> , <code>WakeboardBinding</code> and <code>Tower</code> classes:
<code> public class Wakeboard extends Item {</code>
<code> private String size; </code>
<code> }</code>
<code> </code>
<code> public class WakeboardBinding extends Item {</code>
<code> public class Tower extends Item {</code>
<code> private Fit fit; </code>
<code> private String tubing;</code>
<code> </code>
<code> public static enum Fit { Custom, Exact, Universal }; </code>
Because this example is about type substitution, let's make the inheritance hierarchy a little more interesting by introducing a <code>Wearable</code> abstract
class. <code>Wearable</code> holds the size attribute for both the <code>Wakeboard</code> and <code>WakeboardBinding</code> classes.
The <code>Wearable</code> class is defined as follows:
<code> public abstract class Wearable extends Item { </code>
<code> protected String size; </code>
And the resulting <code>Wakeboard</code> and <code>WakeboardBinding</code> classes
are:
<code> public class Wakeboard extends Wearable { </code>
<code> public class WakeboardBinding extends Wearable { </code>
Also, because the web service manages inventory, you'll want the inventory items to be persisted to a database using the Java Persistence API (sometimes
referred to as JPA). To do this, you need to add an <code>@Entity</code> annotation to each of the classes that will be persisted. The only class that you probably
don't want to persist is the <code>Wearable</code> class. You can add the <code>@MappedSuperclass</code> annotation
to this class so that the JPA will use the attributes of this class for persisting subclasses. Next, you need to add the <code>@Id</code> and the <code>@GeneratedValue(strategy = GenerationType.AUTO)</code> annotations to the <code>Item.Id</code> field. As a result, the field will
be used as the primary key in the database and the Id will be automatically generated if not provided. Finally, because you might add new types of <code>Item</code> s
into the system at a later time, you should add the<code>@Inheritance(strategy=InheritanceType.JOINED)</code> annotation to the <code>Item</code> class.
This will store each subclass in its own database table.
The final data classes look like the following:
<code> @Entity</code>
<code> @Inheritance(strategy=InheritanceType.JOINED)</code>
<code> @Id</code>
<code> @GeneratedValue(strategy = GenerationType.AUTO)</code>
<code> private Long id;</code>
<code> private String itemName;</code>
<code> private double price;</code>
<code> // Getters & setters</code>
<code> @MappedSuperclass</code>
<code> protected String size;</code>
<code> ...</code>
<code> public class Wakeboard extends Wearable {}</code>
<code> public class WakeboardBinding extends Wearable {}</code>
<code> public static enum Fit { Custom, Exact, Universal };</code>
Now that you defined the data model for the application, you can now define the web service interface. Because the application manages information
about wakeboard equipment, let's call the web service <code>WakeRider</code> and let's expose four operations in the web service: <code>addItem</code> ,<code>updateItem</code> , <code>removeItem</code> ,
and <code>getItems</code> .
Here is what the <code>WakerRider</code> class looks
like:
<code> @WebService()</code>
<code> public class WakeRider {</code>
<code> public List<Item> getItems() {...}</code>
<code> </code>
<code> public boolean addItem(Item item) {...}</code>
<code> public boolean updateItem(Item item) {...}</code>
<code> public boolean removeItem(Item item) {...}</code>
If you deployed this web service and then looked at the generated WSDL and schema, you would notice that only the <code>Item</code> type
is defined -- there is no mention of <code>Wearable</code> , <code>Wakeboard</code> , <code>WakeboardBinding</code> ,
or <code>Tower</code> . This is because when JAX-WS introspects the <code>WakeRider</code> class
there is no mention of the other classes. To remedy that you can use the new <code>@XmlSeeAlso</code> annotation and list the other classes that you want to expose
through the <code>WakeRider</code> web service.
Here is what the <code>WakeRider</code> class looks
like with the <code>@XmlSeeAlso</code> annotation:
<code> @XmlSeeAlso({Wakeboard.class, </code>
<code> WakeboardBinding.class, </code>
<code> Tower.class}) </code>
Now when you deploy the <code>WakeRider</code> service
and look at the generated schema, you will see types for <code>Item</code> , <code>Wearable</code> , <code>Wakeboard</code> ,<code>WakeboardBinding</code> ,
and <code>Tower</code> as well as some other types used internally by JAX-WS and JAXB.
@WebResult含義:specifies that the name of the result of the operation in the generated WSDL
targetNamespace含義:The XML namespace used for the WSDL and XML elements generated from this Web Service.
@RequestWrapper 含義:生成的請求包裝器 bean、元素名稱和名稱空間,用于對在運作時使用的請求包裝器 bean 進行序列化和反序列化。
@ResponseWrapper 含義:提供 JAXB 生成的響應包裝器 bean、元素名稱和名稱空間,用于對在運作時使用的響應包裝器 bean 進行序列化和反序列化。
@WebParam 含義:用于定制從單個參數至 Web Service 消息部件和 XML 元素的映射。
@WebMethod annotation :表示作為一項 Web Service 操作的方法。
@WebService annotation: 定義了一個Web Service端點接口(service endpoint interface (SEI)),聲明了一個Client在這個Service上可能invoke 的方法,所有在這個類中定義的public方法都會被映射到WSDL中,除非有一個@WebMethod中設定有排他元素為true的。
5.在Web Service完成後要将相關檔案打包生成WAR檔案,然後将該檔案部署到支援JAX-WS 2.0标準的Web Server上,Java 6有一個輕量級的Web server,通過簡單的API就可以将Web Service釋出。