一、简介
Spring Web Service 致力于开发契约优先的SOAP web Service,可以灵活利用很多方式处理XML内容;契约优先指的是我们在开始编写一个Web Service时,先编写WSDL,或者是XSD文件后动态生成WSDL,而不是先编写java code,然后根据代码生成WSDL。主要的特点有:
1.强大的Mapping功能:你可以基于message内容、SOAP Action header 或者Xpath 表达式将接收到的XML请求映射到任意的Object。
2.支持大量的XML API:不仅仅可以用JAXP APIS 如DOM,SAX,Stax,也可以用 JDOM,dom4j,XOM,甚至直接用编组技术(marshalling technologies,能直接将xml内容转变成java object)操作xml。
3.灵活的XML编组:Spring Web Services 建立在spring framework 的oxm模块,支持JAXB1和JAXB2、Castor、XMLBeans、JiBX以及XStream。
4.支持消息安全机制:Spring WS的安全机制允许你签名SOAP消息,加密和解密,也可以追加认证。
二、示例说明
本示例参照Spring-ws-refenrenc中讲解的例子,实现一个hoiday预定功能,并在此基础上做了一些扩展,采用JAXB2直接将请求Object转变成消息发送,预定成功后返回姓名以及预定号码,并直接将返回结果转变成Object。
三、示例开始,首先编辑XSD
Spring-WS 不推荐直接编写wsdl文件,我们可以编写相对简单的XSD文件,然后生成对应的wsdl文件。
(1) 引用我们自己定义的命名空间下的数据类型
(2)采用xsd:date data type,包含年、月、日
(3)xsd:string、xsd:integer定义对应的属性
(4)xsd:all 表明属性<Holiday/>和<Employee/>的顺序是随意
(5)xsd:sequence 表明属性顺序要保持一致
四、生成wsdl
1.创建动态web工程SpringWebService
2.编辑web.xml
与SpringMvc工程相比,这里只是把DispatcherServlet换成了MessageDispatcherServlet。把transformWsdlLoactions属性设置成true,启动location 转换功能,从而将相对路径动态的转换成绝对路径。
3.编辑context配置文件
(1)指定类扫描路径
(2)标明采用注解驱动
(3)id属性表明了WSDL可以被获取到的URL,这里id是holiday,标明WSDL文件在servlet context中是 holiday.wsdl.完整的获取路径是:http://host:port/工程名/(locationUri)/holiday.wsdl,对应到本示例就是http://localhost:8080/SpringWebService/holidayService/holiday.wsdl;dynamic-wsdl标明在程序执行中动态生成wsdl
(4)指定WSDL的port type是HumanResource
(5)指定了一个相对路径,因为在web.xml中设置了transformWsdlLocations的值为true,所以此处能用相对路径标明wsdl service的访问路径。
(6)指定了WDSL自己的命名空间,如果不设置,wsdl使用和XSD schema相同的命名空间
(7)指定了用来生成wsdl的xsd文件的位置
4.应用启动后,访问对应的url,生成wsdl文件,内容如下
*尽管在运行时从XSDs直接生成WSDL十分便利,采用这种方式会有以下几个问题:
(1)随着Spring-ws版本升级,根据相同的XSD生成的WSDL有可能会发生变化。
(2)即便是只生成一次,然后将wsdl缓存起来,但是因为生成过程也会造成请求变慢。
所以推荐做法是:在development阶段,用<dynamic-wsdl>来动态生成,当发布应用时,利用浏览器将wsdl download下来,然后再用<static-wsdl>,这样就可以保证WSDL保持不变
即把上文的servlet-context.xml中关于sws的部分改为如下内容
五、实现Endpoint
因为采用了JAXB2的组装技术,所以没有了xml的操作,Endpoint实现相当简单
(1)因为SOAP协议和transport无关的,所以Spring-ws不支持通过HTTP请求的URL来Mapping消息到Endpoint,而是通过消息的内容,包括消息的命名空间,消息本地名称,所以PayloadRoot注解中的属性一定要正确,并且和客户端调用时保持一致。
(2)ResponsePayload注解表示有内容返回给客户端
(3)HolidayResponse和HolidayRequest是利用JAXB注解注释的类,可以利用xsd文件直接生成。(此处为了理解xsd到java class直接的转换,我手工写了这两个类,以及它们依赖的类),如何自动生成,可以参照[url]http://fengyilin.iteye.com/admin/blogs/2344183[/url]
以上就是服务端的主要代码,下面简单看下客户端调用
(1)采用编组技术,直接将Java Object转换成消息内容发送
(2)返回结果直接组装成java 对象
完整的工程目录如下:
[img]http://dl2.iteye.com/upload/attachment/0121/8852/98f80d89-37ee-3ae0-ba77-5c794b802898.jpg[/img]
工程的具体代码请参考附件
Spring Web Service 致力于开发契约优先的SOAP web Service,可以灵活利用很多方式处理XML内容;契约优先指的是我们在开始编写一个Web Service时,先编写WSDL,或者是XSD文件后动态生成WSDL,而不是先编写java code,然后根据代码生成WSDL。主要的特点有:
1.强大的Mapping功能:你可以基于message内容、SOAP Action header 或者Xpath 表达式将接收到的XML请求映射到任意的Object。
2.支持大量的XML API:不仅仅可以用JAXP APIS 如DOM,SAX,Stax,也可以用 JDOM,dom4j,XOM,甚至直接用编组技术(marshalling technologies,能直接将xml内容转变成java object)操作xml。
3.灵活的XML编组:Spring Web Services 建立在spring framework 的oxm模块,支持JAXB1和JAXB2、Castor、XMLBeans、JiBX以及XStream。
4.支持消息安全机制:Spring WS的安全机制允许你签名SOAP消息,加密和解密,也可以追加认证。
二、示例说明
本示例参照Spring-ws-refenrenc中讲解的例子,实现一个hoiday预定功能,并在此基础上做了一些扩展,采用JAXB2直接将请求Object转变成消息发送,预定成功后返回姓名以及预定号码,并直接将返回结果转变成Object。
三、示例开始,首先编辑XSD
Spring-WS 不推荐直接编写wsdl文件,我们可以编写相对简单的XSD文件,然后生成对应的wsdl文件。
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:hr="http://fengyilin.com/hr/schemas" elementFormDefault="qualified"
targetNamespace="http://fengyilin.com/hr/schemas">
<xs:element name="HolidayRequest">
<xs:complexType>
<xs:all> (4)
<xs:element name="Holiday" type="hr:HolidayType" /> (1)
<xs:element name="Employee" type="hr:EmployeeType" /> </xs:all>
</xs:complexType>
</xs:element>
<xs:element name="HolidayResponse">
<xs:complexType>
<xs:sequence> (5)
<xs:element name="name" type="xs:string" />
<xs:element name="number" type="xs:integer" /> (3)
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="HolidayType">
<xs:sequence>
<xs:element name="StartDate" type="xs:date" /> (2)
<xs:element name="EndDate" type="xs:date" /> (2)
</xs:sequence>
</xs:complexType>
<xs:complexType name="EmployeeType">
<xs:sequence>
<xs:element name="FirstName" type="xs:string" /> (3)
<xs:element name="LastName" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:schema>
(1) 引用我们自己定义的命名空间下的数据类型
(2)采用xsd:date data type,包含年、月、日
(3)xsd:string、xsd:integer定义对应的属性
(4)xsd:all 表明属性<Holiday/>和<Employee/>的顺序是随意
(5)xsd:sequence 表明属性顺序要保持一致
四、生成wsdl
1.创建动态web工程SpringWebService
2.编辑web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<display-name>SpringWebService</display-name>
<servlet>
<servlet-name>spring-ws</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<init-param>
<param-name>transformWsdlLocations</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>spring-ws</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/resources/log4j/log4j.properties</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
与SpringMvc工程相比,这里只是把DispatcherServlet换成了MessageDispatcherServlet。把transformWsdlLoactions属性设置成true,启动location 转换功能,从而将相对路径动态的转换成绝对路径。
3.编辑context配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:sws="http://www.springframework.org/schema/web-services"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="test.spring.ws.service" /> (1)
<sws:annotation-driven /> (2)
<sws:dynamic-wsdl id="holiday" (3)
portTypeName="HumanResource" (4)
locationUri="/holidayService/" (5)
targetNamespace="http://fengyilin.com/hr/definitions"> (6)
<sws:xsd location="/resources/xsd/hr.xsd" /> (7)
</sws:dynamic-wsdl>
</beans>
(1)指定类扫描路径
(2)标明采用注解驱动
(3)id属性表明了WSDL可以被获取到的URL,这里id是holiday,标明WSDL文件在servlet context中是 holiday.wsdl.完整的获取路径是:http://host:port/工程名/(locationUri)/holiday.wsdl,对应到本示例就是http://localhost:8080/SpringWebService/holidayService/holiday.wsdl;dynamic-wsdl标明在程序执行中动态生成wsdl
(4)指定WSDL的port type是HumanResource
(5)指定了一个相对路径,因为在web.xml中设置了transformWsdlLocations的值为true,所以此处能用相对路径标明wsdl service的访问路径。
(6)指定了WDSL自己的命名空间,如果不设置,wsdl使用和XSD schema相同的命名空间
(7)指定了用来生成wsdl的xsd文件的位置
4.应用启动后,访问对应的url,生成wsdl文件,内容如下
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:sch="http://fengyilin.com/hr/schemas" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://fengyilin.com/hr/definitions" targetNamespace="http://fengyilin.com/hr/definitions">
<wsdl:types>
<xs:schema xmlns:hr="http://fengyilin.com/hr/schemas"
xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
targetNamespace="http://fengyilin.com/hr/schemas">
<xs:element name="HolidayRequest">
<xs:complexType>
<xs:all>
<xs:element name="Holiday" type="hr:HolidayType" />
<xs:element name="Employee" type="hr:EmployeeType" />
</xs:all>
</xs:complexType>
</xs:element>
<xs:element name="HolidayResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="number" type="xs:integer" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="HolidayType">
<xs:sequence>
<xs:element name="StartDate" type="xs:date" />
<xs:element name="EndDate" type="xs:date" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="EmployeeType">
<xs:sequence>
<xs:element name="FirstName" type="xs:string" />
<xs:element name="LastName" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:schema>
</wsdl:types>
<wsdl:message name="HolidayRequest">
<wsdl:part element="sch:HolidayRequest" name="HolidayRequest"></wsdl:part>
</wsdl:message>
<wsdl:message name="HolidayResponse">
<wsdl:part element="sch:HolidayResponse" name="HolidayResponse"></wsdl:part>
</wsdl:message>
<wsdl:portType name="HumanResource">
<wsdl:operation name="Holiday">
<wsdl:input message="tns:HolidayRequest" name="HolidayRequest"></wsdl:input>
<wsdl:output message="tns:HolidayResponse" name="HolidayResponse"></wsdl:output>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="HumanResourceSoap11" type="tns:HumanResource">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="Holiday">
<soap:operation soapAction="" />
<wsdl:input name="HolidayRequest">
<soap:body use="literal" />
</wsdl:input>
<wsdl:output name="HolidayResponse">
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="HumanResourceService">
<wsdl:port binding="tns:HumanResourceSoap11" name="HumanResourceSoap11">
<soap:address location="http://localhost:8080/SpringWebService/holidayService" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
*尽管在运行时从XSDs直接生成WSDL十分便利,采用这种方式会有以下几个问题:
(1)随着Spring-ws版本升级,根据相同的XSD生成的WSDL有可能会发生变化。
(2)即便是只生成一次,然后将wsdl缓存起来,但是因为生成过程也会造成请求变慢。
所以推荐做法是:在development阶段,用<dynamic-wsdl>来动态生成,当发布应用时,利用浏览器将wsdl download下来,然后再用<static-wsdl>,这样就可以保证WSDL保持不变
即把上文的servlet-context.xml中关于sws的部分改为如下内容
<sws:static-wsdl id="holiday" location="/resources/wsdl/holiday.wsdl"/>
五、实现Endpoint
因为采用了JAXB2的组装技术,所以没有了xml的操作,Endpoint实现相当简单
package test.spring.ws.service.endpoint;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.Namespace;
import org.jdom2.filter.Filters;
import org.jdom2.xpath.XPathExpression;
import org.jdom2.xpath.XPathFactory;
import org.springframework.beans.factory.annotation.Autowired;
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;
import org.springframework.xml.xpath.Jaxp13XPathTemplate;
import org.springframework.xml.xpath.XPathOperations;
import test.spring.ws.entity.HolidayRequest;
import test.spring.ws.entity.HolidayResponse;
import test.spring.ws.service.HumanResourceService;
@Endpoint
public class HolidayEndpoint {
private static final String NAMESPACE_URI = "http://fengyilin.com/hr/schemas";
private HumanResourceService humanResourceService;
@Autowired
public HolidayEndpoint(HumanResourceService humanResourceService) throws JDOMException {
this.humanResourceService = humanResourceService;
}
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "HolidayRequest") (1)
@ResponsePayload (2)
public HolidayResponse handleHolidayRequest(@RequestPayload HolidayRequest request) throws Exception { (3)
humanResourceService.bookHoliday(request.getHoliday().getStartDate(), request.getHoliday().getEndDate(),
request.getEmployee().getFirstName());
HolidayResponse response = new HolidayResponse();
response.setName(request.getEmployee().getFirstName()+"_"+request.getEmployee().getLastName());
response.setNumber((int) (100*Math.random()));
return response;
}
}
(1)因为SOAP协议和transport无关的,所以Spring-ws不支持通过HTTP请求的URL来Mapping消息到Endpoint,而是通过消息的内容,包括消息的命名空间,消息本地名称,所以PayloadRoot注解中的属性一定要正确,并且和客户端调用时保持一致。
(2)ResponsePayload注解表示有内容返回给客户端
(3)HolidayResponse和HolidayRequest是利用JAXB注解注释的类,可以利用xsd文件直接生成。(此处为了理解xsd到java class直接的转换,我手工写了这两个类,以及它们依赖的类),如何自动生成,可以参照[url]http://fengyilin.iteye.com/admin/blogs/2344183[/url]
以上就是服务端的主要代码,下面简单看下客户端调用
/*
* Copyright 2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package test.spring.ws.client;
import java.io.IOException;
import java.util.Date;
import javax.xml.transform.Source;
import org.springframework.core.io.ClassPathResource;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.util.ClassUtils;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.xml.transform.ResourceSource;
import org.springframework.xml.transform.StringResult;
import test.spring.ws.entity.EmployeeType;
import test.spring.ws.entity.HolidayRequest;
import test.spring.ws.entity.HolidayResponse;
import test.spring.ws.entity.HolidayType;
import test.spring.ws.jaxb2.JaxbUtil;
public class HolidayClient {
private final WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
// send to the configured default URI
public void sendAndReceive() throws Exception {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setPackagesToScan(new String[] { ClassUtils.getPackageName(HolidayRequest.class) });
marshaller.afterPropertiesSet();
WebServiceTemplate msbServiceTemplate = new WebServiceTemplate(marshaller); -- (1)
HolidayRequest request = new HolidayRequest();
EmployeeType employee = new EmployeeType();
employee.setFirstName("cc");
employee.setLastName("dd");
request.setEmployee(employee);
HolidayType holiday = new HolidayType();
holiday.setEndDate(new Date());
holiday.setStartDate(new Date());
request.setHoliday(holiday);
msbServiceTemplate.setDefaultUri("http://localhost:8080/SpringWebService/services");
HolidayResponse response = (HolidayResponse) msbServiceTemplate.marshalSendAndReceive(request);--(2)
System.out.println("-------");
System.out.println(response);
System.out.println("-------");
}
public static void main(String[] args) throws Exception {
HolidayClient client = new HolidayClient();
client.sendAndReceive();
}
}
(1)采用编组技术,直接将Java Object转换成消息内容发送
(2)返回结果直接组装成java 对象
完整的工程目录如下:
[img]http://dl2.iteye.com/upload/attachment/0121/8852/98f80d89-37ee-3ae0-ba77-5c794b802898.jpg[/img]
工程的具体代码请参考附件