8.2.Webservice示例开发

2023-07-03 17:25 更新
这里我们将介绍两种开发Webservice的方法:一种是基于JAXB2以Javabean对象Endpoint方法的出参与入参;另外一种就是将XMLDOM对象作为Endpoint方法的出参与入参。第一种方法看起来相对复杂,但真正理解后使用会发现其功能强大;第二种方法我们需要熟悉常用的Java XML DOM的解析与创建。两种方法我们对外暴露同一份WSDL文件,因为我们的WSDL从XSD生成,所以我们首先介绍如何编写这份XSD。

8.2.1.定义XSD

对于Spring-WS的contract-first模式来说,开发一个Webservice我们需要从定义一个XSD文件开始。我们要定义的Webservice允许用户发一个请求,当中包含请求的用户数目及用户所在的公司两个信息,Webservice服务在收到这个请求之后,会根据请求的用户数目及所在公司的ID,生成对应的用户,对于生成的用户应该包含四个信息,分别是:用户名、出生日期、性别及所在公司ID,最后Webservice服务将生成的用户集合返回给客户端。
根据上述需求,我们的XSD内容编写如下:
UserSerivce.xsd的内容
<?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内容来看,其中我们采用的namespace为http://www.bstek.com/ws,这个地址是虚拟的,用户在定义时可以灵活选择,在这个XSD内容当中,我们定义了一个名为User的复杂类型对象(complexType),它包含用户名、出生日期、性别及所在公司ID四个element,然后我们还定义两个element对象,分别是UserRequest及UserResponse,其中UserRequest中包含两element,分别是userCount及targetCompany,对应我们的需求就是请求的用户数目及所在公司的ID;另一个UserResponse中只有一个名为users的element,它的类型是我们自定义的User类型,且它的maxOccurs为unbounded表示这个element在UserRequest中数目不限制,对应到我们Java当中这个users是一个集合对象,一个包含User对象的集合对象(可能是一个Collection,也可能是一个数组)。

在Spring-WS当中,在定义XSD时,以Request结尾的命名的element,默认将会被认为是Webservice调用的需要的输入信息(也就是调用参数);而以Response结尾命名的element默认将会被认为是Webservice调用的需要的输出信息(也就是调用返回的结果)。对照这个规则,上述的XSD当中,UserRequest将会作为Webservice的输入参数,而UserResponse则会作为Webservice的调用结果返回。

8.2.2.配置XSD

在编写完成XSD之后,下一步就是将它们配置到Spring环境当中。我们来看看如何配置一个XSD文件,以使其能动态产生我们所需要的用于描述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文件当中添加关于这个sws namespace的相关定义信息,不过默认我们提供的datasources.xml当中已经添加好这个namespace的定义,如下图所示:

在对这个UserService.xsd的配置当中,我们采用的是sws这个namespace提供的dynamic-wsdl,首先它需要指定一个id属性,这个属性很重要,它是我们请求浏览这个XSD文件生成的WSDL重要的凭据,然后指定它的portTypeName属性为DemoUserRequest,这里的portTypeName就是运行时动态生成的wsdl中的portType的name,代码这个服务的名称,接下来是定义locationUri属性,我们这里将其值定义为/dorado/webservice/requestUserWebService,之前我们介绍过,Webservice请求就是一个标准的Http的Post请求,这里将locaionUri属性值设置为/dorado/webservice/requestUserWebService,表示将动态生成的WSDL当中标明目标Webservice的调用地址格式为下面的样子:
http/https://servername:<port>/<contextpath>/dorado/webservice/requestUserWebService

这里需要着重强调的是,对于采用BDF2-WEBSERVICE模块发布的Webservice,在设置这个locationUri属性值是必须要以“/dorado/webservice/”开头,至于后面的值是什么,对于我们这个UserServiceEndpoint来说其实一点也不重要,因为我们这里的UserServiceEndpoint中的getUsers方法是通过请求的SOAP消息的body部分的ROOT节点名及namespace来进行匹配的,与具体的请求地址无关,所以对“/dorado/webservice/”后面是什么就不重要了。

配置好XSD之后,可以启动项目,请求如下格式地址:
http/https://servername:<port>/<contextpath>/dorado/webservice/UserService.wsdl

可以在我们的流程器当中如下图所示的WSDL文档内容:

可以看到在访问这个wsdl时,我们也是以“/dorado/webservice/”开头,后台跟一个UserService.wsdl值,这里需要注意的是要访问wsdl,请求的URL一定要以“.wsdl”结尾,至于前面的UserService,其实就是sws:dynamic-wsdl中的id属性值,所以一旦请求以“/dorado/webservice/”开头同时以“.wsdl”结尾,那么就会有一个由BDF2-WEBSERVICE模块当中提供的专门的Controller负责进行处理,它只会关注.wsdl前面,所以对于上例来说,请求“/dorado/webservice/UserService.wsdl“可以看到WSDL,同样请求"/dorado/webservice/dasfasfd/asfasdf/UserService.wsdl”一样可以看到这个WSDL。

8.2.3.以Javabean作为出入参

以Javabean作为WebService方法的出入参在编写时略显麻烦,但它可以暴露一些非常复杂的Webservice供外部系统调用,一旦我们理解了这种方法,就会觉得这种方法在编写Webservice时更加符合Java的面向对象特性。在写好XSD之后,我们以按照这个XSD当中定义的内容来编写我们Webservice服务当中所涉及的相关Javabean开始来介绍这种方法。

8.2.3.1.根据XSD编写Javabean

根据前面定义的UserService.xsd文件,我们知道,需要定义的JavaBean有三个,分别是User、UserRequest及UserResponse,这三个Javabean采用的namespace、xmlrool、相关属性都已在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用于根据XSD文件中定义的XmlRool及namespace来定义这个User类在序列化成XML之后Xmlroot值及namespace的值;XmlAccessorType用于定义这个bean中属性在序列化成xml之后子节点名及通过什么样方式访问这些属性值,这里定义成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服务类啦,在Spring-WS当中,这个类叫做Endpoint,我们来看看如何编写这个Endpoint。

8.2.3.2.编写Endpoint

从UserService.xsd中定义内容来看,其中有两个Element,一个是UserRequest,一个是UserResponse,根据Spring-WS的默认规范,对于以Request结尾的Element将作为Webservice服务的入参,而对于Response结尾的Element则要作为出参,所以我们即将要编写的Endpoint类中用于接受客户端请求的方法应该包含一个以UserRequest对象的入参,同时该方法还需要返回一个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对象作为入参,同时方法会返回一个UserResponse对象,这个类与其它Javabean不同的地方是它使用一些Spring-WS提供的Annotation,我们首先来看看标注在类名为的Endpoint。
这个名为Endpoint的annotation表示当前这个类将作为Spring-WS的一个Endpoint,它可以接收特定的用户请求,执行其中的业务方法。

在getUsers方法上有个名为@PayloadRoot的Annotation,标明当前这个getUsers方法支持Webservice以SOAP消息的XMLroot节点名及namespace来匹配找到该方法,比如这里定义@PayloadRoot(localPart="UserRequest",namespace="http://www.bstek.com/ws")就表示该方法支持请求Webservice的SOAP消息当中,SOAP消息body部分XML节点名为UserRequest,同时采用http://www.bstek.com/ws作为namespace的SOAP消息,一旦我们的客户端发出的SOAP消息满足上述条件就会执行这里的getUsers方法。

在这个getUsers方法的入参当中有一个UserRequest类型的参数,其前面有个名为@RequestPayload的Annotation,这就表示这个UserRequest值需要从客户端请求的SOAP消息的Body当中解析出来,解析后的Body部分的XML要反序列化成这里需要的UserRequest对象。最后在这个方法的返回值UserResponse前面我们还添加了一个名为@ResponsePayload的Annotation,表示这个返回值将作为响应的负载返回到Webservice调用客户端。

再来看看这个getUsers方法体内部,这里我们只是根据请求信息虚拟了一批用户返回,实际业务中在这个getUsers方法体内部,我们应该去调用需要执行的业务逻辑方法,从而完成Webservice与业务结合的过程。

Endpoint编写完成之后,接下来我们就可以将上述编写XSD及Endpoint类配置到Spring环境当中。

以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号