這里我們將介紹兩種開發(fā)Webservice的方法:一種是基于JAXB2以Javabean對象Endpoint方法的出參與入?yún)?;另外一種就是將XMLDOM對象作為Endpoint方法的出參與入?yún)?。第一種方法看起來相對復(fù)雜,但真正理解后使用會發(fā)現(xiàn)其功能強大;第二種方法我們需要熟悉常用的Java XML DOM的解析與創(chuàng)建。兩種方法我們對外暴露同一份WSDL文件,因為我們的WSDL從XSD生成,所以我們首先介紹如何編寫這份XSD。
8.2.1.定義XSD
對于Spring-WS的contract-first模式來說,開發(fā)一個Webservice我們需要從定義一個XSD文件開始。我們要定義的Webservice允許用戶發(fā)一個請求,當中包含請求的用戶數(shù)目及用戶所在的公司兩個信息,Webservice服務(wù)在收到這個請求之后,會根據(jù)請求的用戶數(shù)目及所在公司的ID,生成對應(yīng)的用戶,對于生成的用戶應(yīng)該包含四個信息,分別是:用戶名、出生日期、性別及所在公司ID,最后Webservice服務(wù)將生成的用戶集合返回給客戶端。
根據(jù)上述需求,我們的XSD內(nèi)容編寫如下:
UserSerivce.xsd的內(nèi)容
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.bstek.com/ws"
xmlns:tns="http://www.bstek.com/ws" elementFormDefault="qualified">
<element name="UserRequest">
<complexType>
<all>
<element name="userCount" type="int"></element>
<element name="targetCompany" type="string"></element>
</all>
</complexType>
</element>
<element name="UserResponse">
<complexType>
<sequence>
<element name="users" maxOccurs="unbounded" type="tns:User"></element>
</sequence>
</complexType>
</element>
<complexType name="User">
<all>
<element name="username" type="string"></element>
<element name="birthday" type="date"></element>
<element name="gender" type="boolean"></element>
<element name="companyId" type="string"></element>
</all>
</complexType>
</schema>
從上面的XSD內(nèi)容來看,其中我們采用的namespace為http://www.bstek.com/ws,這個地址是虛擬的,用戶在定義時可以靈活選擇,在這個XSD內(nèi)容當中,我們定義了一個名為User的復(fù)雜類型對象(complexType),它包含用戶名、出生日期、性別及所在公司ID四個element,然后我們還定義兩個element對象,分別是UserRequest及UserResponse,其中UserRequest中包含兩element,分別是userCount及targetCompany,對應(yīng)我們的需求就是請求的用戶數(shù)目及所在公司的ID;另一個UserResponse中只有一個名為users的element,它的類型是我們自定義的User類型,且它的maxOccurs為unbounded表示這個element在UserRequest中數(shù)目不限制,對應(yīng)到我們Java當中這個users是一個集合對象,一個包含User對象的集合對象(可能是一個Collection,也可能是一個數(shù)組)。
在Spring-WS當中,在定義XSD時,以Request結(jié)尾的命名的element,默認將會被認為是Webservice調(diào)用的需要的輸入信息(也就是調(diào)用參數(shù));而以Response結(jié)尾命名的element默認將會被認為是Webservice調(diào)用的需要的輸出信息(也就是調(diào)用返回的結(jié)果)。對照這個規(guī)則,上述的XSD當中,UserRequest將會作為Webservice的輸入?yún)?shù),而UserResponse則會作為Webservice的調(diào)用結(jié)果返回。
8.2.2.配置XSD
在編寫完成XSD之后,下一步就是將它們配置到Spring環(huán)境當中。我們來看看如何配置一個XSD文件,以使其能動態(tài)產(chǎn)生我們所需要的用于描述Webservice的wsdl文檔。
打開我們的Spring配置文件,我們這里就采用WEB-INF/dorado-home目錄下的datasources.xml文件進行配置,在這個datasources.xml文件當中添加如下配置信息:
XSD文件在Spring當中的配置
<sws:dynamic-wsdl id="UserService" portTypeName="DemoUserRequest" locationUri="/dorado/webservice/requestUserWebService">
<sws:xsd location="classpath:ws/UserService.xsd"/>
</sws:dynamic-wsdl>
在上述配置當中,我們采用了一個名為sws的namespace,要使用這個namespace,我們需要在這個datasources.xml文件當中添加關(guān)于這個sws namespace的相關(guān)定義信息,不過默認我們提供的datasources.xml當中已經(jīng)添加好這個namespace的定義,如下圖所示:

在對這個UserService.xsd的配置當中,我們采用的是sws這個namespace提供的dynamic-wsdl,首先它需要指定一個id屬性,這個屬性很重要,它是我們請求瀏覽這個XSD文件生成的WSDL重要的憑據(jù),然后指定它的portTypeName屬性為DemoUserRequest,這里的portTypeName就是運行時動態(tài)生成的wsdl中的portType的name,代碼這個服務(wù)的名稱,接下來是定義locationUri屬性,我們這里將其值定義為/dorado/webservice/requestUserWebService,之前我們介紹過,Webservice請求就是一個標準的Http的Post請求,這里將locaionUri屬性值設(shè)置為/dorado/webservice/requestUserWebService,表示將動態(tài)生成的WSDL當中標明目標Webservice的調(diào)用地址格式為下面的樣子:
http/https://servername:<port>/<contextpath>/dorado/webservice/requestUserWebService
這里需要著重強調(diào)的是,對于采用BDF2-WEBSERVICE模塊發(fā)布的Webservice,在設(shè)置這個locationUri屬性值是必須要以“/dorado/webservice/”開頭,至于后面的值是什么,對于我們這個UserServiceEndpoint來說其實一點也不重要,因為我們這里的UserServiceEndpoint中的getUsers方法是通過請求的SOAP消息的body部分的ROOT節(jié)點名及namespace來進行匹配的,與具體的請求地址無關(guān),所以對“/dorado/webservice/”后面是什么就不重要了。
配置好XSD之后,可以啟動項目,請求如下格式地址:
http/https://servername:<port>/<contextpath>/dorado/webservice/UserService.wsdl
可以在我們的流程器當中如下圖所示的WSDL文檔內(nèi)容:

可以看到在訪問這個wsdl時,我們也是以“/dorado/webservice/”開頭,后臺跟一個UserService.wsdl值,這里需要注意的是要訪問wsdl,請求的URL一定要以“.wsdl”結(jié)尾,至于前面的UserService,其實就是sws:dynamic-wsdl中的id屬性值,所以一旦請求以“/dorado/webservice/”開頭同時以“.wsdl”結(jié)尾,那么就會有一個由BDF2-WEBSERVICE模塊當中提供的專門的Controller負責進行處理,它只會關(guān)注.wsdl前面,所以對于上例來說,請求“/dorado/webservice/UserService.wsdl“可以看到WSDL,同樣請求"/dorado/webservice/dasfasfd/asfasdf/UserService.wsdl”一樣可以看到這個WSDL。
8.2.3.以Javabean作為出入?yún)?/h3>
以Javabean作為WebService方法的出入?yún)⒃诰帉憰r略顯麻煩,但它可以暴露一些非常復(fù)雜的Webservice供外部系統(tǒng)調(diào)用,一旦我們理解了這種方法,就會覺得這種方法在編寫Webservice時更加符合Java的面向?qū)ο筇匦?。在寫好XSD之后,我們以按照這個XSD當中定義的內(nèi)容來編寫我們Webservice服務(wù)當中所涉及的相關(guān)Javabean開始來介紹這種方法。
8.2.3.1.根據(jù)XSD編寫Javabean
根據(jù)前面定義的UserService.xsd文件,我們知道,需要定義的JavaBean有三個,分別是User、UserRequest及UserResponse,這三個Javabean采用的namespace、xmlrool、相關(guān)屬性都已在UserService.xsd當中標明,我們需要做的就是按UserService.xsd文件定義的信息來編寫Javabean即可。首先來看看User類,其源碼如下:
User類源碼
package ws;
import java.util.Date;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name="User",namespace="http://www.bstek.com/ws")
@XmlAccessorType(XmlAccessType.FIELD)
public class User{
private String username;
private Date birthday;
private boolean gender;
private String companyId;
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 getCompanyId() {
return companyId;
}
public void setCompanyId(String companyId) {
this.companyId = companyId;
}
}
在這個User類當中,我們添加了兩個Annotation,分別是XmlRootElement及XmlAccessorType,其中XmlRootElement用于根據(jù)XSD文件中定義的XmlRool及namespace來定義這個User類在序列化成XML之后Xmlroot值及namespace的值;XmlAccessorType用于定義這個bean中屬性在序列化成xml之后子節(jié)點名及通過什么樣方式訪問這些屬性值,這里定義成XmlAccessType.FIELD,表示直接采用屬性名方式。
UserRequest與UserResponse與User類定義方式相同,全部是嚴格按照UserService.xsd定義編寫,它們的源碼如下:
UserRequest源碼
package ws;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name="UserRequest",namespace="http://www.bstek.com/ws")
@XmlAccessorType(XmlAccessType.FIELD)
public class UserRequest {
private int userCount;
private String targetCompany;
public int getUserCount() {
return userCount;
}
public void setUserCount(int userCount) {
this.userCount = userCount;
}
public String getTargetCompany() {
return targetCompany;
}
public void setTargetCompany(String targetCompany) {
this.targetCompany = targetCompany;
}
}
UserResponse源碼
package ws;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name="UserResponse",namespace="http://www.bstek.com/ws")
@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;
}
}
Javabean定義完成之后,接下來就可以定義負責具體接受客戶端請求的Webservice服務(wù)類啦,在Spring-WS當中,這個類叫做Endpoint,我們來看看如何編寫這個Endpoint。
8.2.3.2.編寫Endpoint
從UserService.xsd中定義內(nèi)容來看,其中有兩個Element,一個是UserRequest,一個是UserResponse,根據(jù)Spring-WS的默認規(guī)范,對于以Request結(jié)尾的Element將作為Webservice服務(wù)的入?yún)?,而對于Response結(jié)尾的Element則要作為出參,所以我們即將要編寫的Endpoint類中用于接受客戶端請求的方法應(yīng)該包含一個以UserRequest對象的入?yún)?,同時該方法還需要返回一個UserResponse對象,明確這一點之后,接下來就可以編寫我們的Endpoint類了。我們的UserServiceEndpoint類源碼如下:
UserServiceEndpoint類源碼
package ws;
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{
@PayloadRoot(localPart="UserRequest",namespace="http://www.bstek.com/ws")
public @ResponsePayload UserResponse getUsers(@RequestPayload UserRequest request){
int userCount=request.getUserCount();
String targetCompanyId=request.getTargetCompany();
UserResponse response=new UserResponse();
List<User> users=new ArrayList<User>();
for(int i=0;i<userCount;i++){
User user=new User();
user.setBirthday(new Date());
user.setCompanyId(targetCompanyId);
user.setGender(true);
user.setUsername("user"+i);
users.add(user);
}
response.setUsers(users);
return response;
}
}
在這個UserServiceEndpoint類當中,只有一個getUsers方法,可以看到這個方法需要一個UserRequest對象作為入?yún)ⅲ瑫r方法會返回一個UserResponse對象,這個類與其它Javabean不同的地方是它使用一些Spring-WS提供的Annotation,我們首先來看看標注在類名為的Endpoint。
這個名為Endpoint的annotation表示當前這個類將作為Spring-WS的一個Endpoint,它可以接收特定的用戶請求,執(zhí)行其中的業(yè)務(wù)方法。
在getUsers方法上有個名為@PayloadRoot的Annotation,標明當前這個getUsers方法支持Webservice以SOAP消息的XMLroot節(jié)點名及namespace來匹配找到該方法,比如這里定義@PayloadRoot(localPart="UserRequest",namespace="http://www.bstek.com/ws")就表示該方法支持請求Webservice的SOAP消息當中,SOAP消息body部分XML節(jié)點名為UserRequest,同時采用http://www.bstek.com/ws作為namespace的SOAP消息,一旦我們的客戶端發(fā)出的SOAP消息滿足上述條件就會執(zhí)行這里的getUsers方法。
在這個getUsers方法的入?yún)斨杏幸粋€UserRequest類型的參數(shù),其前面有個名為@RequestPayload的Annotation,這就表示這個UserRequest值需要從客戶端請求的SOAP消息的Body當中解析出來,解析后的Body部分的XML要反序列化成這里需要的UserRequest對象。最后在這個方法的返回值UserResponse前面我們還添加了一個名為@ResponsePayload的Annotation,表示這個返回值將作為響應(yīng)的負載返回到Webservice調(diào)用客戶端。
再來看看這個getUsers方法體內(nèi)部,這里我們只是根據(jù)請求信息虛擬了一批用戶返回,實際業(yè)務(wù)中在這個getUsers方法體內(nèi)部,我們應(yīng)該去調(diào)用需要執(zhí)行的業(yè)務(wù)邏輯方法,從而完成Webservice與業(yè)務(wù)結(jié)合的過程。
Endpoint編寫完成之后,接下來我們就可以將上述編寫XSD及Endpoint類配置到Spring環(huán)境當中。
更多建議: