概述
我們這里要編寫一個WebService的Endpoint,它可以接收一個UserRequest對象,這個對象當中只包含一個userCount的參數(shù),Endpoint接收到參數(shù)后,會產(chǎn)生與userCount相同數(shù)量的User對象,最后將這個User對象的集合放在一個名為UserResponse的對象當中作為Endpoint的調(diào)用響應發(fā)送給調(diào)用這個WebService的客戶端。接下來我們先從編寫WebService服務端開始,看看如何實現(xiàn)這個WebService示例。
WebService服務端開發(fā)
因為在我們的WebService服務端當中引入了JAXB2,所以整個服務端Endpoint類編寫變的簡單,同時Spring-WS2.0支持將XSD文檔自動轉(zhuǎn)換成WSDL,這樣我們就不用直接編寫復雜的WSDL,而只需要編寫好XSD文檔即可實現(xiàn)WSDL發(fā)布。一般來說,一個WebService的編寫是從定義WSDL開始的,對于我們這里來說,因為我們可以將XSD自動轉(zhuǎn)換成WSDL,所以我們就是從編寫簡單的XSD開始。
關(guān)于XSD文檔的語法規(guī)范,如果您不熟悉,可以從http://www.w3school.com.cn/schema/index.asp上面了解。
我們這里規(guī)定,所有的進站參數(shù)在定義XSD時要以Request結(jié)尾,比如我們這里的UserRequest;所有的出站參數(shù)要以Response結(jié)尾,比如我們這里的UserResponse。了解這些之后就可以定義們的XSD文檔,具體內(nèi)容如下:
<?xml version= "1.0" encoding= "UTF-8" ?>
<element name= "UserResponse" >
<complexType>
<sequence>
<element name= "users" type= "tns:User" maxOccurs= "unbounded" ></element>
</sequence>
</complexType>
</element>
<element name= "UserRequest" >
<complexType>
<all>
<element name= "userCount" type= "int" ></element>
</all>
</complexType>
</element>
<complexType name= "User" >
<sequence>
<element name= "username" type= "string" ></element>
<element name= "birthday" type= "date" ></element>
<element name= "gender" type= "boolean" ></element>
<element name= "email" type= "string" ></element>
<element name= "dept" type= "tns:Dept" ></element>
</sequence>
</complexType>
<complexType name= "Dept" >
<sequence>
<element name= "deptId" type= "string" ></element>
<element name= "deptName" type= "string" ></element>
</sequence>
</complexType>
</schema>
|
XSD文檔的編寫是非常簡單的,如果您使用的IDE是Eclipse,那么您可以直接使用Eclipse當中提供了XSD圖形化編輯器來編寫這個XSD文檔。從文檔內(nèi)容中可以看到,我們定義消息的namespace為http://www./ws/demo,同時定義了一個名為UserRequest的Element為進站參數(shù);一個名為UserResponse的Element為出站參數(shù)。
XSD文檔編寫完成之后,我們就可以利用Spring-WS提供的配置功能將XSD直接轉(zhuǎn)換成我們需要的WSDL。
因為我們采用的是Spring-WS為基礎實現(xiàn)WebService,所以一旦XSD文檔編寫完成我們就可以通過配置的方式將XSD轉(zhuǎn)換成WSDL,新建一個Spring配置文件,在文件當中添加如下內(nèi)容:
<?xml version= "1.0" encoding= "UTF-8" ?>
xsi:schemaLocation="http:
http:
http:
<context:component-scan base- package = "com.bstek.bdf.webservice.demo" ></context:component-scan>
<sws:dynamic-wsdl id= "demo" portTypeName= "UserResource" locationUri= "/webservice/demo" >
<sws:xsd location= "classpath:com/bstek/bdf/webservice/demo/demo.xsd" />
</sws:dynamic-wsdl>
</beans>
|
我們需要關(guān)注的就是這個配置文件當中以sws:dynamic-wsdl開頭的配置片斷,這里采用位于classpath下的一個xsd文檔,發(fā)布成WSDL的ID為demo,同時客戶端調(diào)用這個WebService的地址我們定義為/webservice/demo。啟用我們的應用,打開瀏覽器,可以瀏覽到這個動態(tài)生成的名為demo的wsdl文檔。如下圖所示。

WSDL發(fā)布成功之后,接下來我們就需要動手來編寫我們的WebService服務端的Endpoint,由這個EndPoint來接收客戶端的調(diào)用請求。
Endpoint實際上是WebService真正的服務類,我們只需要按照之前發(fā)布的WSDL規(guī)則來編寫即可,由它來接收客戶端請求,同時將響應回寫到客戶端當中。因為Spring提供了Endpoint的annotation,同時我們又添加了JAXB2支持,所以Endpoint類編寫是非常簡單的。下面是我們編寫的與之前發(fā)布的WSDL對應的Endpoint類源碼:
package com.bstek.bdf.webservice.demo;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
@Endpoint
public class UserServiceEndpoint{
@ResponsePayload
public UserResponse findUsers( @RequestPayload UserRequest request) throws Exception{
List<User> userList= new ArrayList<User>();
for ( int i= 0 ;i<request.getUserCount();i++){
User u= new User();
u.setBirthday( new Date());
u.setGender( true );
u.setUsername( "從WS中取到的User " +i);
u.setEmail( "bstek.user" +i+ "." );
userList.add(u);
}
for ( int i= 0 ;i< 100 ;i++){
Thread.sleep( 50 );
}
UserResponse res= new UserResponse();
res.setUsers(userList);
return res;
}
}
|
可以看到,我們這里采用了四個annotation,在類上的@Endpoint標明我們將這個類發(fā)布成一個Endpoint服務類,findUsers方法上有兩個annotation:@PayloadRoot用于標明findUser可以授受的XML信息,這里取的是XML的namespace為http://www./ws/demo,同時XML的ROOT為UserRequest的信息;下面的@ResponsePayload標明findUsers方法有返回值,返回值需要回寫給Webservice調(diào)用客戶端;參數(shù)中還有一個@RequestPayload的annotation,它表示從請求負載中取值作為參數(shù),我們這里因為采用了JAXB2,所以可以將負載的XML消息直接轉(zhuǎn)換成一個名為UserRequest的Java對象。
從findUsers方法體中可以看到,其中都是針對Java對象的操作,是非常簡單的。這是因為我們添加了JAXB2支持的原因,如果沒有我們這里就只能操作復雜的XML了。
我們這里涉及到的需要使用JAXB2實現(xiàn)Java對象與XML相互轉(zhuǎn)換的類有下面幾個:
- UserRequest
package com.bstek.bdf.webservice.demo;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType (XmlAccessType.FIELD)
public class UserRequest {
private int userCount;
public int getUserCount() {
return userCount;
}
public void setUserCount( int userCount) {
this .userCount = userCount;
}
}
UserResponse
package com.bstek.bdf.webservice.demo;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
@XmlAccessorType (XmlAccessType.FIELD)
public class UserResponse {
private List<User> users;
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this .users = users;
}
}
|
User
package com.bstek.bdf.webservice.demo;
import java.util.Date;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType (XmlAccessType.FIELD)
public class User {
private String username;
private Date birthday;
private boolean gender;
private String email;
private Dept dept;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this .username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this .birthday = birthday;
}
public boolean isGender() {
return gender;
}
public void setGender( boolean gender) {
this .gender = gender;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this .email = email;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this .dept = dept;
}
@Override
public String toString() {
return "User [username=" + username + ", birthday=" + birthday
+ ", gender=" + gender + ", email=" + email + ", dept=" + dept
+ "]" ;
}
}
|
可以看到,這三個類都是非常普通的POJO對象,它們都用到了@XmlRootElement、@XmlAccessorType,這兩個annotation都是JAXB2提供,要了解詳情請查看JAXB2相關(guān)文檔。
到這里為止,我們的Endpoint編寫就完成了,因為我們使用了Spring-WS提供的Endpoint的annotation,所以不需要在Spring配置當中進行任何配置,唯一需要的就是添加一個context:component-seca即可,這樣Spring會自動掃描位于base-package屬性指定包下所有的Endpoint類。
可以看到,這三個類都是非常普通的POJO對象,它們都用到了@XmlRootElement、@XmlAccessorType,這兩個annotation都是JAXB2提供,要了解詳情請查看JAXB2相關(guān)文檔。
到這里為止,我們的Endpoint編寫就完成了,因為我們使用了Spring-WS提供的Endpoint的annotation,所以不需要在Spring配置當中進行任何配置,唯一需要的就是添加一個context:component-seca即可,這樣Spring會自動掃描位于base-package屬性指定包下所有的Endpoint類。
< context:component-scan base-package = "com.bstek.bdf.webservice.demo" ></ context:component-scan >
|
WebService的服務端我們開發(fā)完成,接下來我們就來看看客戶端如何對這個服務端進行調(diào)用。
WebService客戶端調(diào)用
前面提到過,我們?yōu)榱藢崿F(xiàn)快速調(diào)用目標WebService服務端,我們提供了一個名為WebServiceClient對象,通過它可以快速實現(xiàn)調(diào)用目標WebService服務端;當然你也可以使用諸如SOAPUI之類客戶端工具實現(xiàn)對目標WebService調(diào)用測試。
我們這里以之前編寫的服務端為類,看看WebServiceClient對象如何調(diào)用這個WebService。具體代碼如下:
package com.bstek.bdf.webservice.demo;
import com.bstek.bdf.webservice.client.WebServiceClient;
public class DemoWebserviceClient {
public static void main(String[] args) throws Exception{
WebServiceClient client= new WebServiceClient();
client.setUsernameToken( "admin" , "admin" , true );
client.setMarshallerUnmarshallerClass( new Class[]{User. class ,UserResponse. class ,UserRequest. class });
UserRequest request= new UserRequest();
request.setUserCount( 20 );
UserResponse response=(UserResponse)client.marshalSendAndReceive(request);
int i= 1 ;
for (User user:response.getUsers()){
System.out.println( "webservice 產(chǎn)生的用戶" +i+ ":" +user);
i++;
}
}
}
|
我們已在代碼當中進行了注釋,這里就不再解釋。一旦調(diào)用完成(不管是成功還是失?。覀兛梢栽赽df_webservice_logs表中看到日志記錄情況。同時在默認情況下我們沒有啟用權(quán)限功能,所以所有用戶名密碼相同的用戶都可以進行調(diào)用。
關(guān)于WebServiceClient類還有一個方法, 詳情可查看WebServiceClient的javadoc。