Spring-WS 的服务器端支持围绕一个 MessageDispatcher 设计,该调度器将传入消息分派给端点,并可配置端点映射、响应生成和端点拦截。端点通常使用 @Endpoint 注解,并且有一个或多个处理方法。这些方法通过检查消息的各个部分(通常是有效负载)来处理传入的 XML 请求消息,并创建某种响应。您可以使用另一个注解(通常是 @PayloadRoot)来注解该方法,以指示它可以处理哪种类型的消息。
Spring-WS 的 XML 处理非常灵活。一个端点可以从 Spring-WS 支持的大量 XML 处理库中进行选择,包括 DOM 系列(W3C DOM、JDOM、dom4j 和 XOM)、用于更快性能的 SAX 或 StAX、用于从消息中提取信息的 XPath,甚至编组技术(JAXB、Castor、XMLBeans、JiBX 或 XStream)来将 XML 转换为对象,反之亦然。
Spring-WS 的服务器端围绕一个中心类设计,该类将传入的 XML 消息分派给端点。Spring-WS 的 MessageDispatcher 非常灵活,允许您使用任何类型的类作为端点,只要它可以在 Spring IoC 容器中进行配置。从某种意义上说,消息调度器类似于 Spring 的 DispatcherServlet,即 Spring Web MVC 中使用的“前端控制器”。
MessageDispatcher 的处理和分派流程如下图所示。

Spring Web Services 中的请求处理工作流程
当 MessageDispatcher 设置就绪并收到针对该特定调度器的请求时,该 MessageDispatcher 将开始处理请求。下面的列表描述了由 MessageDispatcher 处理请求的完整过程。
使用配置的 EndpointMapping(s) 查找适当的端点。如果找到端点,将执行与该端点关联的调用链(预处理器、后处理器和端点)以创建响应。
为端点搜索适当的适配器。MessageDispatcher 将调用委派给此适配器以调用端点。
如果返回响应,则将其发送。如果没有返回响应(例如,由于预处理器或后处理器拦截请求,例如出于安全原因),则不发送响应。
在处理请求期间抛出的异常将被应用程序上下文中声明的任何端点异常解析器捕获。使用这些异常解析器允许您在抛出此类异常时定义自定义行为(例如返回 SOAP 故障)。
MessageDispatcher 具有多个属性,用于设置端点适配器、映射、异常解析器。但是,设置这些属性不是必需的,因为调度器将自动检测应用程序上下文中注册的所有这些类型。只有在需要覆盖检测时,才应设置这些属性。
消息调度器操作在消息上下文上,而不是特定于传输的输入流和输出流。因此,特定于传输的请求需要读取到 MessageContext 中。对于 HTTP,这是通过 WebServiceMessageReceiverHandlerAdapter 完成的,它是一个 Spring Web HandlerInterceptor,以便 MessageDispatcher 可以以标准 DispatcherServlet 的方式进行连接。但是,有一种更方便的方法可以做到这一点,如第 5.3.1 节,“MessageDispatcherServlet”所示。
Spring Web Services 支持多种传输协议。最常见的是 HTTP 传输,为此提供了一个自定义 servlet,但也可以通过 JMS 甚至电子邮件发送消息。
MessageDispatcherServlet 是一个标准的 Servlet,它方便地扩展了标准的 Spring Web DispatcherServlet,并包装了一个 MessageDispatcher。因此,它将这些属性合并为一个:作为一个 MessageDispatcher,它遵循与上一节中描述的相同的请求处理流程。作为一个 servlet,MessageDispatcherServlet 在您的 Web 应用程序的 web.xml 中配置。您希望 MessageDispatcherServlet 处理的请求将必须使用相同的 web.xml 文件中的 URL 映射进行映射。这是标准的 Java EE servlet 配置;下面可以找到此类 MessageDispatcherServlet 声明和映射的示例。
<web-app>
<servlet>
<servlet-name>spring-ws</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-ws</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
在上面的示例中,所有请求都将由 'spring-ws' MessageDispatcherServlet 处理。这只是设置 Spring Web Services 的第一步,因为 Spring-WS 框架使用的各种组件 bean 也需要配置;此配置由标准 Spring XML <bean/> 定义组成。因为 MessageDispatcherServlet 是一个标准的 Spring DispatcherServlet,它将在您的 Web 应用程序的 WEB-INF 目录中查找名为 [servlet-name]-servlet.xml 的文件,并在 Spring 容器中创建其中定义的 bean。在上面的示例中,这意味着它查找“/WEB-INF/spring-ws-servlet.xml”。此文件将包含所有 Spring Web Services bean,例如端点、编组器等。
MessageDispatcherServlet 将自动检测其 Spring 容器中定义的任何 WsdlDefinition bean。所有检测到的此类 WsdlDefinition bean 也将通过 WsdlDefinitionHandlerAdapter 暴露;这是一种非常方便的方式,只需定义一些 bean 即可将 WSDL 暴露给客户端。
举例来说,考虑以下在 Spring-WS 配置文件(/WEB-INF/[servlet-name]-servlet.xml)中定义的 <static-wsdl> 定义。请注意“id”属性的值,因为这将在暴露 WSDL 时使用。
<sws:static-wsdl id="orders" location="/WEB-INF/wsdl/orders.wsdl"/>
然后,可以通过对以下形式的 URL(根据需要替换主机、端口和 servlet 上下文路径)进行 GET 请求来访问“Orders.wsdl”文件中定义的 WSDL。
https://:8080/spring-ws/orders.wsdl
所有 WsdlDefinition bean 定义都由 MessageDispatcherServlet 以其 bean id(或 bean 名称)加上 .wsdl 后缀的形式公开。因此,如果 bean id 是 echo,主机名是“server”,Servlet 上下文(war 名称)是“spring-ws”,则可以通过 http://server/spring-ws/echo.wsdl 获取 WSDL
MessageDispatcherServlet(或者更确切地说,WsdlDefinitionHandlerAdapter)的另一个很好的特性是它能够转换它暴露的所有 WSDL 的“location”值以反映传入请求的 URL。
请注意,此“location”转换功能默认是关闭的。要启用此功能,您只需为 MessageDispatcherServlet 指定一个初始化参数,如下所示:
<web-app>
<servlet>
<servlet-name>spring-ws</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
<init-param>
<param-name>transformWsdlLocations</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>spring-ws</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
请查阅 WsdlDefinitionHandlerAdapter 类的类级别 Javadoc,以了解有关整个转换过程的更多信息。
除了手动编写 WSDL 并使用 <static-wsdl> 暴露它之外,Spring Web Services 还可以从 XSD 模式生成 WSDL。这是第 3.7 节,“发布 WSDL”中显示的方法。以下应用程序上下文片段显示了如何创建此类动态 WSDL 文件
<sws:dynamic-wsdl id="orders"
portTypeName="Orders"
locationUri="https://:8080/ordersService/">
<sws:xsd location="/WEB-INF/xsd/Orders.xsd"/>
</sws:dynamic-wsdl>
<dynamic-wsdl> 通过使用约定从 XSD 模式构建 WSDL。它遍历模式中找到的所有 element 元素,并为所有元素创建一个 message。接下来,它为所有以定义的请求或响应后缀结尾的消息创建 WSDL operation。默认的请求后缀是 Request;默认的响应后缀是 Response,尽管这些可以通过分别设置 <dynamic-wsdl /> 上的 requestSuffix 和 responseSuffix 属性来更改。它还根据操作构建 portType、binding 和 service。
例如,如果我们的 Orders.xsd 模式定义了 GetOrdersRequest 和 GetOrdersResponse 元素,则 <dynamic-wsdl> 将创建一个 GetOrdersRequest 和 GetOrdersResponse 消息,以及一个 GetOrders 操作,该操作位于 Orders 端口类型中。
如果您想通过包含或导入使用多个模式,您需要将 Commons XMLSchema 放在类路径上。如果 Commons XMLSchema 在类路径上,则上述 <dynamic-wsdl> 元素将遵循所有 XSD 导入和包含,并将它们作为单个 XSD 内联到 WSDL 中。这大大简化了模式的部署,同时仍然可以单独编辑它们。
<dynamic-wsdl> 元素依赖于 DefaultWsdl11Definition 类。此定义类使用 org.springframework.ws.wsdl.wsdl11.provider 包中的 WSDL 提供程序和 ProviderBasedWsdl4jDefinition 在首次请求时生成 WSDL。如有必要,请参阅这些类的类级别 Javadoc 以了解如何扩展此机制。
尽管从 XSD 实时创建 WSDL 非常方便,但这种方法也有一些缺点。首先,尽管我们努力在不同版本之间保持 WSDL 生成过程的一致性,但它仍然有可能(略微)发生变化。其次,生成速度有点慢,尽管一旦生成,WSDL 会被缓存以供以后参考。
因此,建议仅在项目开发阶段使用 <dynamic-wsdl>。然后,我们建议使用浏览器下载生成的 WSDL,将其存储在项目中,并使用 <static-wsdl> 暴露它。这是真正确保 WSDL 不随时间变化的唯一方法。
作为 MessageDispatcherServlet 的替代方案,您可以在标准的 Spring-Web MVC DispatcherServlet 中连接 MessageDispatcher。默认情况下,DispatcherServlet 只能委托给 Controllers,但我们可以通过向 servlet 的 Web 应用程序上下文添加 WebServiceMessageReceiverHandlerAdapter 来指示它委托给 MessageDispatcher
<beans>
<bean class="org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter"/>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="defaultHandler" ref="messageDispatcher"/>
</bean
<bean id="messageDispatcher" class="org.springframework.ws.soap.server.SoapMessageDispatcher"/>
...
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
</beans>请注意,通过显式添加 WebServiceMessageReceiverHandlerAdapter,调度器 servlet 不会加载默认适配器,并且无法处理标准 Spring-MVC Controllers。因此,我们在末尾添加 SimpleControllerHandlerAdapter。
以类似的方式,您可以连接 WsdlDefinitionHandlerAdapter 以确保 DispatcherServlet 可以处理 WsdlDefinition 接口的实现
<beans>
<bean class="org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter"/>
<bean class="org.springframework.ws.transport.http.WsdlDefinitionHandlerAdapter"/>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="*.wsdl">myServiceDefinition</prop>
</props>
</property>
<property name="defaultHandler" ref="messageDispatcher"/>
</bean>
<bean id="messageDispatcher" class="org.springframework.ws.soap.server.SoapMessageDispatcher"/>
<bean id="myServiceDefinition" class="org.springframework.ws.wsdl.wsdl11.SimpleWsdl11Definition">
<prop name="wsdl" value="/WEB-INF/myServiceDefintion.wsdl"/>
</bean>
...
</beans>
Spring Web Services 通过 Spring 框架中提供的 JMS 功能支持服务器端 JMS 处理。Spring Web Services 提供了 WebServiceMessageListener 以插入 MessageListenerContainer。此消息侦听器需要 WebServiceMessageFactory 和 MessageDispatcher 才能运行。以下配置片段显示了这一点
<beans>
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="vm://?broker.persistent=false"/>
</bean>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destinationName" value="RequestQueue"/>
<property name="messageListener">
<bean class="org.springframework.ws.transport.jms.WebServiceMessageListener">
<property name="messageFactory" ref="messageFactory"/>
<property name="messageReceiver" ref="messageDispatcher"/>
</bean>
</property>
</bean>
<bean id="messageDispatcher" class="org.springframework.ws.soap.server.SoapMessageDispatcher">
<property name="endpointMappings">
<bean
class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
<property name="defaultEndpoint">
<bean class="com.example.MyEndpoint"/>
</property>
</bean>
</property>
</bean>
</beans>
作为 WebServiceMessageListener 的替代方案,Spring Web Services 提供了 WebServiceMessageDrivenBean,一个 EJB MessageDrivenBean。有关 EJB 的更多信息,请参阅 WebServiceMessageDrivenBean 的类级别 Javadoc。
除了 HTTP 和 JMS,Spring Web Services 还提供服务器端电子邮件处理。此功能通过 MailMessageReceiver 类提供。该类监视 POP3 或 IMAP 文件夹,将电子邮件转换为 WebServiceMessage,并使用 SMTP 发送任何响应。主机名可以通过 storeUri 进行配置,它指示要监视请求的邮件文件夹(通常是 POP3 或 IMAP 文件夹),以及 transportUri,它指示用于发送响应的服务器(通常是 SMTP 服务器)。
MailMessageReceiver 如何监视传入消息可以通过可插拔策略进行配置:MonitoringStrategy。默认情况下,使用轮询策略,每五分钟轮询一次传入文件夹以获取新消息。此间隔可以通过设置策略上的 pollingInterval 属性来更改。默认情况下,所有 MonitoringStrategy 实现都删除已处理的消息;这可以通过设置 deleteMessages 属性来更改。
作为效率较低的轮询方法的替代方案,有一种使用 IMAP IDLE 的监视策略。IDLE 命令是 IMAP 电子邮件协议的可选扩展,允许邮件服务器异步地向 MailMessageReceiver 发送新消息更新。如果您使用支持 IDLE 命令的 IMAP 服务器,您可以将 ImapIdleMonitoringStrategy 插入到 monitoringStrategy 属性中。除了支持的服务器,您还需要使用 JavaMail 1.4.1 或更高版本。
以下配置片段显示了如何使用服务器端电子邮件支持,将默认轮询间隔覆盖为每 30 秒检查一次(30,000 毫秒)的值
<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="messagingReceiver" class="org.springframework.ws.transport.mail.MailMessageReceiver">
<property name="messageFactory" ref="messageFactory"/>
<property name="from" value="Spring-WS SOAP Server <[email protected]>"/>
<property name="storeUri" value="imap://server:[email protected]/INBOX"/>
<property name="transportUri" value="smtp://smtp.example.com"/>
<property name="messageReceiver" ref="messageDispatcher"/>
<property name="monitoringStrategy">
<bean class="org.springframework.ws.transport.mail.monitor.PollingMonitoringStrategy">
<property name="pollingInterval" value="30000"/>
</bean>
</property>
</bean>
<bean id="messageDispatcher" class="org.springframework.ws.soap.server.SoapMessageDispatcher">
<property name="endpointMappings">
<bean
class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
<property name="defaultEndpoint">
<bean class="com.example.MyEndpoint"/>
</property>
</bean>
</property>
</bean>
</beans>
Spring Web Services 提供了一种基于 Sun JRE 1.6 HTTP 服务器的传输。嵌入式 HTTP 服务器是一个独立的服务器,配置简单。它是一种比传统 servlet 容器更轻量级的替代方案。
使用嵌入式 HTTP 服务器时,不需要外部部署描述符(web.xml)。您只需要定义服务器实例并将其配置为处理传入请求。Core Spring Framework 中的 remoting 模块包含 HTTP 服务器的便捷工厂 bean:SimpleHttpServerFactoryBean。最重要的属性是 contexts,它将上下文路径映射到相应的 HttpHandler。
Spring Web Services 提供了 HttpHandler 接口的 2 种实现:WsdlDefinitionHttpHandler 和 WebServiceMessageReceiverHttpHandler。前者将传入的 GET 请求映射到 WsdlDefinition。后者负责处理 Web 服务消息的 POST 请求,因此需要 WebServiceMessageFactory(通常是 SaajSoapMessageFactory)和 WebServiceMessageReceiver(通常是 SoapMessageDispatcher)来完成其任务。
为了与 servlet 世界进行类比,contexts 属性扮演着 web.xml 中 servlet 映射的角色,而 WebServiceMessageReceiverHttpHandler 相当于 MessageDispatcherServlet。
以下片段显示了 HTTP 服务器传输的简单配置示例
<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="messageReceiver" class="org.springframework.ws.soap.server.SoapMessageDispatcher">
<property name="endpointMappings" ref="endpointMapping"/>
</bean>
<bean id="endpointMapping" class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
<property name="defaultEndpoint" ref="stockEndpoint"/>
</bean>
<bean id="httpServer" class="org.springframework.remoting.support.SimpleHttpServerFactoryBean">
<property name="contexts">
<map>
<entry key="/StockService.wsdl" value-ref="wsdlHandler"/>
<entry key="/StockService" value-ref="soapHandler"/>
</map>
</property>
</bean>
<bean id="soapHandler" class="org.springframework.ws.transport.http.WebServiceMessageReceiverHttpHandler">
<property name="messageFactory" ref="messageFactory"/>
<property name="messageReceiver" ref="messageReceiver"/>
</bean>
<bean id="wsdlHandler" class="org.springframework.ws.transport.http.WsdlDefinitionHttpHandler">
<property name="definition" ref="wsdlDefinition"/>
</bean>
</beans>
有关 SimpleHttpServerFactoryBean 的更多信息,请参阅 Javadoc。
最后,Spring Web Services 2.0 引入了对 XMPP 的支持,也称为 Jabber。此支持基于 Smack 库。
Spring Web Services 对 XMPP 的支持与其他传输非常相似:WebServiceTemplate 有一个 XmppMessageSender,MessageDispatcher 有一个 XmppMessageReceiver。
以下示例展示了如何设置服务器端 XMPP 组件
<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="connection" class="org.springframework.ws.transport.xmpp.support.XmppConnectionFactoryBean">
<property name="host" value="jabber.org"/>
<property name="username" value="username"/>
<property name="password" value="password"/>
</bean>
<bean id="messagingReceiver" class="org.springframework.ws.transport.xmpp.XmppMessageReceiver">
<property name="messageFactory" ref="messageFactory"/>
<property name="connection" ref="connection"/>
<property name="messageReceiver" ref="messageDispatcher"/>
</bean>
<bean id="messageDispatcher" class="org.springframework.ws.soap.server.SoapMessageDispatcher">
<property name="endpointMappings">
<bean
class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
<property name="defaultEndpoint">
<bean class="com.example.MyEndpoint"/>
</property>
</bean>
</property>
</bean>
</beans>
端点是 Spring-WS 服务器端支持中的核心概念。端点提供对通常由业务服务接口定义的应用程序行为的访问。端点解释 XML 请求消息并使用该输入(通常)调用业务服务上的方法。该服务调用的结果表示为响应消息。Spring-WS 有各种各样的端点,使用各种方式处理 XML 消息并创建响应。
您通过使用 @Endpoint 注解一个类来创建端点。在该类中,您定义一个或多个处理传入 XML 请求的方法,通过使用各种参数类型(例如 DOM 元素、JAXB2 对象等)。您通过使用另一个注解(通常是 @PayloadRoot)来指示方法可以处理哪种类型的消息。
考虑以下示例端点
package samples; import org.w3c.dom.Element; 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.soap.SoapHeader; @Endpointpublic class AnnotationOrderEndpoint { private final OrderService orderService; @Autowired
public AnnotationOrderEndpoint(OrderService orderService) { this.orderService = orderService; } @PayloadRoot(localPart = "order", namespace = "http://samples")
public void order(@RequestPayload Element orderElement) {
Order order = createOrder(orderElement); orderService.createOrder(order); } @PayloadRoot(localPart = "orderRequest", namespace = "http://samples")
@ResponsePayload public Order getOrder(@RequestPayload OrderRequest orderRequest, SoapHeader header) {
checkSoapHeaderForSomething(header); return orderService.getOrder(orderRequest.getId()); } ... }
|
该类使用 |
|
构造函数用 |
|
有关端点方法的更多信息,请参阅第 5.4.1 节,“ |
|
有关端点方法的更多信息,请参阅第 5.4.1 节,“ |
|
此端点的两个处理方法都用 有关 |
要启用对 @Endpoint 和相关 Spring-WS 注解的支持,您需要在 Spring 应用程序上下文中添加以下内容
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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"> <sws:annotation-driven /> </beans>
在接下来的几节中,将更详细地描述 @Endpoint 编程模型。
端点,像任何其他 Spring Bean 一样,默认情况下被作用域为单例,即每个容器创建一个 bean 定义实例。作为单例意味着多个线程可以同时使用它,因此端点必须是线程安全的。如果您想使用不同的作用域,例如原型,请参阅 Spring 参考文档。
请注意,Spring-WS 中提供的所有抽象基类都是线程安全的,除非类级别 Javadoc 中另有说明。
为了让端点实际处理传入的 XML 消息,它需要有一个或多个处理方法。处理方法可以接受各种参数和返回类型,但通常它们有一个参数将包含消息有效负载,并返回响应消息的有效负载(如果有)。您将在本节中了解支持哪些参数和返回类型。
为了指示方法可以处理哪种类型的消息,该方法通常使用 @PayloadRoot 或 @SoapAction 注解进行注解。您将在第 5.5 节,“端点映射”中了解有关这些注解的更多信息。
这是一个处理方法的示例
@PayloadRoot(localPart = "order", namespace = "http://samples")
public void order(@RequestPayload Element orderElement) {
Order order = createOrder(orderElement);
orderService.createOrder(order);
}order 方法将一个 Element 作为参数,并用 @RequestPayload 注解。这意味着消息的有效负载作为 DOM 元素传递给此方法。该方法的返回类型为 void,表示不发送响应消息。
处理方法通常有一个或多个参数,它们引用传入 XML 消息的各个部分。最常见的是,处理方法将有一个映射到消息有效负载的单个参数,但也可以映射到请求消息的其他部分,例如 SOAP 头。本节将描述您可以在处理方法签名中使用的参数。
要将参数映射到请求消息的有效负载,您需要使用 @RequestPayload 注解此参数。此注解告诉 Spring-WS 该参数需要绑定到请求有效负载。
下表描述了支持的参数类型。它显示了支持的类型,参数是否应使用 @RequestPayload 进行注解,以及任何其他注意事项。
| 名称 | 支持的参数类型 | 需要 @RequestPayload 吗? | 附加说明 |
|---|---|---|---|
| TrAX |
javax.xml.transform.Source 及其子接口(DOMSource、SAXSource、StreamSource 和 StAXSource) | ✓ | 默认启用。 |
| W3C DOM | org.w3c.dom.Element | ✓ | 默认启用 |
| dom4j | org.dom4j.Element | ✓ | 当 dom4j 在类路径上时启用。 |
| JDOM | org.jdom.Element | ✓ | 当 JDOM 在类路径上时启用。 |
| XOM | nu.xom.Element | ✓ | 当 XOM 在类路径上时启用。 |
| StAX |
javax.xml.stream.XMLStreamReader 和 javax.xml.stream.XMLEventReader | ✓ | 当 StAX 在类路径上时启用。 |
| XPath | 任何 boolean, double, String, org.w3c.Node, org.w3c.dom.NodeList,或可以通过 Spring 3 转换服务从 String 转换的类型,并且用 @XPathParam 注解。 | ✗ | 默认启用,请参阅第 5.4.1.1.1 节,“@XPathParam”。 |
| 消息上下文 | org.springframework.ws.context.MessageContext | ✗ | 默认启用。 |
| SOAP |
org.springframework.ws.soap.SoapMessage、org.springframework.ws.soap.SoapBody、org.springframework.ws.soap.SoapEnvelope、org.springframework.ws.soap.SoapHeader 和 org.springframework.ws.soap.SoapHeaderElement 在与 @SoapHeader 注解结合使用时。 | ✗ | 默认启用。 |
| JAXB2 | 任何用 javax.xml.bind.annotation.XmlRootElement 和 javax.xml.bind.JAXBElement 注解的类型。 | ✓ | 当 JAXB2 在类路径上时启用。 |
| OXM | Spring OXM Unmarshaller 支持的任何类型。 | ✓ | 当指定了 <sws:annotation-driven/> 的 unmarshaller 属性时启用。 |
以下是一些可能的方法签名示例
public void handle(@RequestPayload Element element)
此方法将以请求消息的有效负载作为 DOM org.w3c.dom.Element 进行调用。
public void handle(@RequestPayload DOMSource domSource, SoapHeader header)
此方法将以请求消息的有效负载作为 javax.xml.transform.dom.DOMSource 进行调用。header 参数将绑定到请求消息的 SOAP 头。
public void handle(@RequestPayload MyJaxb2Object requestObject, @RequestPayload Element element, Message messageContext)
此方法将以请求消息的有效负载解组为 MyJaxb2Object(用 @XmlRootElement 注解)进行调用。消息的有效负载也作为 DOM Element 给出。整个消息上下文作为第三个参数传递。
如您所见,定义处理方法签名有很多可能性。甚至可以扩展此机制,并支持您自己的参数类型。请参阅 DefaultMethodEndpointAdapter 和 MethodArgumentResolver 的类级别 Javadoc 以了解如何操作。
一种参数类型需要额外解释:@XPathParam。这里的想法是您只需使用 XPath 表达式注解一个或多个方法参数,并且每个此类注解的参数都将绑定到表达式的求值。这是一个示例
package samples;
import javax.xml.transform.Source;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.Namespace;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.XPathParam;
@Endpoint
public class AnnotationOrderEndpoint {
private final OrderService orderService;
public AnnotationOrderEndpoint(OrderService orderService) {
this.orderService = orderService;
}
@PayloadRoot(localPart = "orderRequest", namespace = "http://samples")
@Namespace(prefix = "s", uri="http://samples")
public Order getOrder(@XPathParam("/s:orderRequest/@id") int orderId) {
Order order = orderService.getOrder(orderId);
// create Source from order and return it
}
}
由于我们在 XPath 表达式中使用了前缀“s”,因此我们必须将其绑定到 http://samples 命名空间。这通过 @Namespace 注解完成。或者,我们可以将此注解放在类型级别,以对所有处理程序方法使用相同的命名空间映射,甚至包级别(在 package-info.java 中)以将其用于多个端点。
使用 @XPathParam,您可以绑定到 XPath 支持的所有数据类型
boolean 或 Boolean
double 或 Double
字符串
节点
NodeList
除了此列表之外,您还可以使用任何可以通过 Spring 3 转换服务从 String 转换的类型。
要发送响应消息,处理程序需要指定返回类型。如果不需要响应消息,方法可以简单地声明 void 返回类型。最常见的是,返回类型用于创建响应消息的有效负载,但也可以映射到响应消息的其他部分。本节将描述您可以在处理方法签名中使用的返回类型。
要将返回值映射到响应消息的有效负载,您需要使用 @ResponsePayload 注解该方法。此注解告诉 Spring-WS 返回值需要绑定到响应有效负载。
下表描述了支持的返回类型。它显示了支持的类型,参数是否应使用 @ResponsePayload 进行注解,以及任何其他注意事项。
| 名称 | 支持的返回类型 | 需要 @ResponsePayload 吗? | 附加说明 |
|---|---|---|---|
| 无响应 |
void
| ✗ | 默认启用。 |
| TrAX |
javax.xml.transform.Source 及其子接口(DOMSource、SAXSource、StreamSource 和 StAXSource) | ✓ | 默认启用。 |
| W3C DOM | org.w3c.dom.Element | ✓ | 默认启用 |
| dom4j | org.dom4j.Element | ✓ | 当 dom4j 在类路径上时启用。 |
| JDOM | org.jdom.Element | ✓ | 当 JDOM 在类路径上时启用。 |
| XOM | nu.xom.Element | ✓ | 当 XOM 在类路径上时启用。 |
| JAXB2 | 任何用 javax.xml.bind.annotation.XmlRootElement 和 javax.xml.bind.JAXBElement 注解的类型。 | ✓ | 当 JAXB2 在类路径上时启用。 |
| OXM | Spring OXM Marshaller 支持的任何类型。 | ✓ | 当指定了 <sws:annotation-driven/> 的 marshaller 属性时启用。 |
如您所见,定义处理方法签名有很多可能性。甚至可以扩展此机制,并支持您自己的参数类型。请参阅 DefaultMethodEndpointAdapter 和 MethodReturnValueHandler 的类级别 Javadoc 以了解如何操作。
端点映射负责将传入消息映射到适当的端点。有一些开箱即用的端点映射,例如 PayloadRootAnnotationMethodEndpointMapping 或 SoapActionAnnotationMethodEndpointMapping,但我们首先研究 EndpointMapping 的一般概念。
一个 EndpointMapping 交付一个 EndpointInvocationChain,它包含与传入请求匹配的端点,并且可能还包含将应用于请求和响应的端点拦截器列表。当请求传入时,MessageDispatcher 会将其交给端点映射,让它检查请求并提出一个适当的 EndpointInvocationChain。然后 MessageDispatcher 将调用端点和链中的任何拦截器。
可配置端点映射的概念(可以可选地包含拦截器(可以操作请求或响应,或两者))功能非常强大。许多支持功能可以内置到自定义 EndpointMapping 中。例如,可能有一个自定义端点映射,它不仅根据消息内容选择端点,还根据特定的 SOAP 头(或多个 SOAP 头)选择端点。
大多数端点映射都继承自 AbstractEndpointMapping,它提供了一个“interceptors”属性,它是要使用的拦截器列表。EndpointInterceptor 在第 5.5.2 节,“拦截请求 - EndpointInterceptor 接口”中讨论。此外,还有“defaultEndpoint”,当此端点映射未产生匹配的端点时,它是要使用的默认端点。
如第 5.4 节,“端点”中所述,@Endpoint 样式允许您在一个端点类中处理多个请求。这是 MethodEndpointMapping 的责任。此映射确定对传入请求消息调用哪个方法。
有两种端点映射可以将请求定向到方法:PayloadRootAnnotationMethodEndpointMapping 和 SoapActionAnnotationMethodEndpointMapping,两者都通过在应用程序上下文中使用 <sws:annotation-driven/> 启用。
PayloadRootAnnotationMethodEndpointMapping 使用 @PayloadRoot 注解,以及 localPart 和 namespace 元素,以特定限定名称标记方法。每当传入的消息的有效负载根元素具有此限定名称时,将调用该方法。例如,请参见上文。
或者,SoapActionAnnotationMethodEndpointMapping 使用 @SoapAction 注解来标记具有特定 SOAP Action 的方法。每当传入的消息具有此 SOAPAction 头时,将调用该方法。
WS-Addressing 指定了一种与传输无关的路由机制。它基于 To 和 Action SOAP 头部,分别指示 SOAP 消息的目的地和意图。此外,WS-Addressing 允许您定义返回地址(用于正常消息和故障),以及可用于关联的唯一消息标识符[2]。以下是 WS-Addressing 消息的示例
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope"
xmlns:wsa="http://www.w3.org/2005/08/addressing">
<SOAP-ENV::Header>
<wsa:MessageID>urn:uuid:21363e0d-2645-4eb7-8afd-2f5ee1bb25cf</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>http://example.com/business/client1</wsa:Address>
</wsa:ReplyTo>
<wsa:To S:mustUnderstand="true">http://example/com/fabrikam</wsa:To>
<wsa:Action>http://example.com/fabrikam/mail/Delete</wsa:Action>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<f:Delete xmlns:f="http://example.com/fabrikam">
<f:maxCount>42</f:maxCount>
</f:Delete>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>在此示例中,目的地设置为 http://example/com/fabrikam,而操作设置为 http://example.com/fabrikam/mail/Delete。此外,还有一个消息标识符和回复地址。默认情况下,此地址是“匿名”地址,表示应使用与请求相同的通道(即 HTTP 响应)发送响应,但它也可以是另一个地址,如本示例所示。
在 Spring Web Services 中,WS-Addressing 实现为一个端点映射。使用此映射,您将 WS-Addressing 动作与端点关联,类似于上面描述的 SoapActionAnnotationMethodEndpointMapping。
AnnotationActionEndpointMapping 类似于 SoapActionAnnotationMethodEndpointMapping,但使用 WS-Addressing 头而不是 SOAP Action 传输头。
要使用 AnnotationActionEndpointMapping,请使用 @Action 注解处理方法,类似于第 5.4.1 节,“@Endpoint 处理方法”和第 5.5 节,“端点映射”中描述的 @PayloadRoot 和 @SoapAction 注解。这是一个示例
package samples;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.soap.addressing.server.annotation.Action
@Endpoint
public class AnnotationOrderEndpoint {
private final OrderService orderService;
public AnnotationOrderEndpoint(OrderService orderService) {
this.orderService = orderService;
}
@Action("http://samples/RequestOrder")
public Order getOrder(OrderRequest orderRequest) {
return orderService.getOrder(orderRequest.getId());
}
@Action("http://samples/CreateOrder")
public void order(Order order) {
orderService.createOrder(order);
}
}
上述映射将 WS-Addressing Action 为 http://samples/RequestOrder 的请求路由到 getOrder 方法。具有 http://samples/CreateOrder 的请求将路由到 order 方法。
默认情况下,AnnotationActionEndpointMapping 支持 WS-Addressing 的 1.0 (2006 年 5 月) 和 2004 年 8 月版。这两个版本最受欢迎,并与 Axis 1 和 2、JAX-WS、XFire、Windows Communication Foundation (WCF) 和 Windows Services Enhancements (WSE) 3.0 互操作。如有必要,可以将特定版本的规范注入到 versions 属性中。
除了 @Action 注解之外,您还可以使用 @Address 注解该类。如果设置,该值将与传入消息的 To 头部属性进行比较。
最后,有 messageSenders 属性,这是将响应消息发送到非匿名、越界地址所必需的。您可以在此属性中设置 MessageSender 实现,就像在 WebServiceTemplate 上一样。请参阅第 6.2.1.1 节,“URI 和传输”。
端点映射机制具有端点拦截器的概念。当您想要将特定功能应用于某些请求时,这些功能非常有用,例如,处理与安全相关的 SOAP 标头,或记录请求和响应消息。
端点拦截器通常通过在应用程序上下文中,使用 <sws;interceptors > 元素定义。在此元素中,您可以简单地定义适用于该应用程序上下文中定义的所有端点的端点拦截器 bean。或者,您可以使用 <sws:payloadRoot> 或 <sws:soapAction> 元素来指定拦截器应适用于哪个有效负载根名称或 SOAP 操作。例如
<sws:interceptors>
<bean class="samples.MyGlobalInterceptor"/>
<sws:payloadRoot namespaceUri="http://www.example.com">
<bean class="samples.MyPayloadRootInterceptor"/>
</sws:payloadRoot>
<sws:soapAction value="http://www.example.com/SoapAction">
<bean class="samples.MySoapActionInterceptor1"/>
<ref bean="mySoapActionInterceptor2"/>
</sws:soapAction>
</sws:interceptors>
<bean id="mySoapActionInterceptor2" class="samples.MySoapActionInterceptor2"/>这里,我们定义了一个“全局”拦截器(MyGlobalInterceptor),它拦截所有请求和响应。我们还定义了一个拦截器,它只适用于有效负载根命名空间为 http://www.example.com 的 XML 消息。在这里,除了 namespaceUri 之外,我们还可以定义一个 localPart 属性,以进一步限制拦截器适用的消息。最后,我们定义了两个拦截器,当消息具有 http://www.example.com/SoapAction SOAP 操作时应用。请注意,第二个拦截器实际上是对 <interceptors> 元素之外的 bean 定义的引用。您可以在 <interceptors> 元素内的任何地方使用 bean 引用。
拦截器必须实现 org.springframework.ws.server 包中的 EndpointInterceptor 接口。此接口定义了三个方法,一个用于在实际端点执行之前处理请求消息,一个用于处理正常响应消息,另一个用于处理故障消息,这两个方法都将在端点执行之后调用。这三个方法应提供足够的灵活性来执行各种预处理和后处理。
拦截器上的 handleRequest(..) 方法返回一个布尔值。您可以使用此方法中断或继续处理调用链。当此方法返回 true 时,端点执行链将继续;当它返回 false 时,MessageDispatcher 将其解释为拦截器本身已处理好事情,并且不会继续执行调用链中的其他拦截器和实际端点。handleResponse(..) 和 handleFault(..) 方法也具有布尔返回值。当这些方法返回 false 时,响应将不会发送回客户端。
您可以在 Web 服务中使用一些标准的 EndpointInterceptor 实现。此外,还有 XwsSecurityInterceptor,在第 7.2 节,“XwsSecurityInterceptor”中进行了描述。
在开发 Web 服务时,记录传入和传出 XML 消息可能很有用。SWS 通过 PayloadLoggingInterceptor 和 SoapEnvelopeLoggingInterceptor 类实现了这一点。前者将消息的有效负载记录到 Commons Logging Log 中;后者记录整个 SOAP 包,包括 SOAP 头。以下示例显示了如何在端点映射中定义它们
<sws:interceptors>
<bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>
</sws:interceptors>
</beans>
这两个拦截器都有两个属性:“logRequest”和“logResponse”,可以设置为 false 以禁用请求或响应消息的日志记录。
使用契约优先开发风格的好处之一是我们可以使用模式来验证传入和传出 XML 消息。Spring-WS 通过 PayloadValidatingInterceptor 实现了这一点。此拦截器需要引用一个或多个 W3C XML 或 RELAX NG 模式,并且可以设置为验证请求或响应,或两者。
请注意,请求验证听起来是个好主意,但会使最终的 Web 服务非常严格。通常,请求是否验证并不真正重要,只在端点能否获得足够的信息来完成请求时才重要。验证响应是个好主意,因为端点应该遵循其模式。记住波斯特尔定律:“对你所做的要保守,对你从别人那里接受的要开放。”
这是一个使用 PayloadValidatingInterceptor 的示例;在此示例中,我们使用 /WEB-INF/orders.xsd 中的模式来验证响应,但不验证请求。请注意,PayloadValidatingInterceptor 还可以使用 schemas 属性接受多个模式。
<bean id="validatingInterceptor"
class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
<property name="schema" value="/WEB-INF/orders.xsd"/>
<property name="validateRequest" value="false"/>
<property name="validateResponse" value="true"/>
</bean>
为了将有效负载转换为另一种 XML 格式,Spring Web Services 提供了 PayloadTransformingInterceptor。此端点拦截器基于 XSLT 样式表,在支持多个 Web 服务版本时特别有用:您可以将旧的消息格式转换为新格式。以下是使用 PayloadTransformingInterceptor 的示例
<bean id="transformingInterceptor"
class="org.springframework.ws.server.endpoint.interceptor.PayloadTransformingInterceptor">
<property name="requestXslt" value="/WEB-INF/oldRequests.xslt"/>
<property name="responseXslt" value="/WEB-INF/oldResponses.xslt"/>
</bean>
我们只是使用 /WEB-INF/oldRequests.xslt 转换请求,并使用 /WEB-INF/oldResponses.xslt 转换响应消息。请注意,由于端点拦截器在端点映射级别注册,您可以简单地创建一个适用于“旧样式”消息的端点映射,并将拦截器添加到该映射中。因此,转换将仅适用于这些“旧样式”消息。
Spring-WS 提供了 EndpointExceptionResolvers,以缓解当消息由匹配请求的端点处理时发生的意外异常的痛苦。端点异常解析器有点类似于在 Web 应用程序描述符 web.xml 中定义的异常映射。但是,它们提供了一种更灵活的方式来处理异常。它们提供有关抛出异常时调用了哪个端点的信息。此外,程序化处理异常的方式为您提供了更多选项来适当响应。您可以使用您想要的任何方式处理异常,例如通过返回具有特定故障代码和字符串的 SOAP 故障,而不是通过给出异常和堆栈跟踪来暴露应用程序内部。
端点异常解析器由 MessageDispatcher 自动拾取,因此无需显式配置。
除了实现 EndpointExceptionResolver 接口(这只是实现 resolveException(MessageContext, endpoint, Exception) 方法的问题)之外,您还可以使用其中一个提供的实现。最简单的实现是 SimpleSoapExceptionResolver,它只创建一个 SOAP 1.1 Server 或 SOAP 1.2 Receiver Fault,并使用异常消息作为故障字符串。SimpleSoapExceptionResolver 是默认的,但可以通过显式添加另一个解析器来覆盖它。
SoapFaultMappingExceptionResolver 是一个更复杂的实现。此解析器使您能够获取可能抛出的任何异常的类名并将其映射到 SOAP 故障,如下所示
<beans>
<bean id="exceptionResolver"
class="org.springframework.ws.soap.server.endpoint.SoapFaultMappingExceptionResolver">
<property name="defaultFault" value="SERVER"/>
<property name="exceptionMappings">
<value>
org.springframework.oxm.ValidationFailureException=CLIENT,Invalid request
</value>
</property>
</bean>
</beans>
键值和默认端点使用 faultCode,faultString,locale 格式,其中只有故障代码是必需的。如果未设置故障字符串,则默认为异常消息。如果未设置语言,则默认为英语。上述配置将 ValidationFailureException 类型的异常映射到客户端 SOAP 故障,其故障字符串为 "Invalid request",如下面的响应所示
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Client</faultcode>
<faultstring>Invalid request</faultstring>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
如果发生任何其他异常,它将返回默认故障:一个服务器端故障,其故障字符串为异常消息。
最后,还可以使用 @SoapFault 注解异常类,以指示在抛出该异常时应返回的 SOAP 故障。为了使这些注解被拾取,您需要在应用程序上下文中添加 SoapFaultAnnotationExceptionResolver。注解的元素包括故障代码枚举、故障字符串或原因以及语言。这是一个示例异常
package samples;
import org.springframework.ws.soap.server.endpoint.annotation.FaultCode;
import org.springframework.ws.soap.server.endpoint.annotation.SoapFault;
@SoapFault(faultCode = FaultCode.SERVER)
public class MyBusinessException extends Exception {
public MyClientException(String message) {
super(message);
}
}
每当在端点调用期间抛出带有构造函数字符串 "Oops!" 的 MyBusinessException 时,它将导致以下响应
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring>Oops!</faultstring>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
在测试 Web 服务端点时,有两种可能的方法
编写单元测试,您为端点提供(模拟)参数以供消费。
这种方法的优点是它非常容易实现(特别是对于用 @Endpoint 注解的类);缺点是您没有真正测试通过网络发送的 XML 消息的确切内容。
编写集成测试,它确实测试消息的内容。
第一种方法可以使用 EasyMock、JMock 等模拟框架轻松实现。下一节将重点介绍如何使用 Spring Web Services 2.0 中引入的测试功能编写集成测试。
Spring Web Services 2.0 引入了对创建端点集成测试的支持。在此上下文中,端点是处理(SOAP)消息的类(参见第 5.4 节,“端点”)。
集成测试支持位于 org.springframework.ws.test.server 包中。该包中的核心类是 MockWebServiceClient。基本思想是,此客户端创建请求消息,然后将其发送到在标准 MessageDispatcherServlet 应用程序上下文中配置的端点(参见第 5.3.1 节,“MessageDispatcherServlet”)。这些端点将处理消息并创建响应。然后客户端接收此响应,并根据注册的期望进行验证。
MockWebServiceClient 的典型用法是
通过调用 MockWebServiceClient.createClient(ApplicationContext) 或 MockWebServiceClient.createClient(WebServiceMessageReceiver, WebServiceMessageFactory) 创建 MockWebServiceClient 实例。
通过调用 sendRequest(RequestCreator) 发送请求消息,可能通过使用 RequestCreators 中提供的默认 RequestCreator 实现(可以静态导入)。
通过调用 andExpect(ResponseMatcher) 设置响应期望,可能通过使用 ResponseMatchers 中提供的默认 ResponseMatcher 实现(可以静态导入)。可以通过链式调用 andExpect(ResponseMatcher) 来设置多个期望。
请注意,MockWebServiceClient(及相关类)提供了一个“流畅”的 API,因此您通常可以使用 IDE 中的代码完成功能(即 ctrl-space)来指导您完成设置模拟服务器的过程。
另请注意,您在单元测试中依赖 Spring Web Services 中可用的标准日志记录功能。有时,检查请求或响应消息以找出特定测试失败的原因可能很有用。有关更多信息,请参阅第 4.4 节,“消息日志记录和跟踪”。
例如,考虑这个简单的 Web 服务端点类
import org.springframework.ws.server.endpoint.annotation.Endpoint; import org.springframework.ws.server.endpoint.annotation.RequestPayload; import org.springframework.ws.server.endpoint.annotation.ResponsePayload; @Endpointpublic class CustomerEndpoint { @ResponsePayload
public CustomerCountResponse getCustomerCount(
@RequestPayload CustomerCountRequest request) {
CustomerCountResponse response = new CustomerCountResponse(); response.setCustomerCount(10); return response; } }
|
|
|
|
CustomerEndpoint 的典型测试如下所示
import javax.xml.transform.Source; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.xml.transform.StringSource; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.ws.test.server.MockWebServiceClient;import static org.springframework.ws.test.server.RequestCreators.*;
import static org.springframework.ws.test.server.ResponseMatchers.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("spring-ws-servlet.xml")
public class CustomerEndpointIntegrationTest { @Autowired private ApplicationContext applicationContext;
private MockWebServiceClient mockClient; @Before public void createClient() { mockClient = MockWebServiceClient.createClient(applicationContext);
} @Test public void customerEndpoint() throws Exception { Source requestPayload = new StringSource( "<customerCountRequest xmlns='http://springframework.org/spring-ws'>" + "<customerName>John Doe</customerName>" + "</customerCountRequest>"); Source responsePayload = new StringSource( "<customerCountResponse xmlns='http://springframework.org/spring-ws'>" + "<customerCount>10</customerCount>" + "</customerCountResponse>"); mockClient.sendRequest(withPayload(requestPayload)).
andExpect(payload(responsePayload));
} }
|
|
|
此测试使用 Spring Framework 中提供的标准测试功能。这不是必需的,但通常是设置测试最简单的方法。 |
|
应用程序上下文是一个标准的 Spring-WS 应用程序上下文(参见第 5.3.1 节,“ |
|
在 |
|
我们通过调用 我们还通过调用 测试的这一部分可能看起来有点令人困惑,但 IDE 的代码完成功能非常有帮助。在键入 |
最初,MockWebServiceClient 需要为端点创建请求消息以供消费。客户端为此目的使用 RequestCreator 策略接口
public interface RequestCreator {
WebServiceMessage createRequest(WebServiceMessageFactory messageFactory)
throws IOException;
}
您可以编写此接口的自己的实现,通过使用消息工厂创建请求消息,但您当然不必这样做。RequestCreators 类提供了一种基于给定有效负载通过 withPayload() 方法创建 RequestCreator 的方法。您通常会静态导入 RequestCreators。
当请求消息由端点处理并收到响应后,MockWebServiceClient 可以验证此响应消息是否符合某些期望。客户端为此目的使用 ResponseMatcher 策略接口
public interface ResponseMatcher {
void match(WebServiceMessage request,
WebServiceMessage response)
throws IOException, AssertionError;
}
再次,您可以编写此接口的自己的实现,当消息不符合您的期望时抛出 AssertionError,但您当然不必这样做,因为 ResponseMatchers 类为您提供了标准 ResponseMatcher 实现,供您在测试中使用。您通常会静态导入此类。
ResponseMatchers 类提供了以下响应匹配器
ResponseMatchers 方法 | 描述 |
|---|---|
payload() | 预期给定的响应有效负载。 |
validPayload() | 预期响应有效负载将针对给定的 XSD 模式进行验证。 |
xpath() | 期望给定的 XPath 表达式存在,不存在,或评估为给定值。 |
soapHeader() | 预期响应消息中存在给定的 SOAP 头。 |
noFault() | 预期响应消息不包含 SOAP 故障。 |
mustUnderstandFault()、clientOrSenderFault()、serverOrReceiverFault() 和 versionMismatchFault() | 预期响应消息包含特定的 SOAP 故障。 |
您可以通过链式调用 andExpect() 来设置多个响应期望,如下所示
mockClient.sendRequest(...). andExpect(payload(expectedResponsePayload)). andExpect(validPayload(schemaResource));
有关 ResponseMatchers 提供的请求匹配器的更多信息,请参阅类级别 Javadoc。