Spring-WS 的服务器端支持围绕一个 MessageDispatcher
设计,该 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 服务中的请求处理工作流
当 MessageDispatcher
设置为使用并且针对该特定分派器传入请求时,该 MessageDispatcher
开始处理请求。以下列表描述了由 MessageDispatcher
处理的请求经历的完整过程。
使用配置的 EndpointMapping(s)
搜索合适的端点。如果找到端点,则将按顺序执行与端点关联的调用链(预处理器、后处理器和端点)以创建响应。
为端点搜索合适的适配器。MessageDispatcher
将委托给此适配器以调用端点。
如果返回响应,则将其发送出去。如果未返回响应(例如,由于预处理器或后处理器拦截了请求(出于安全原因),则不会发送任何响应。
在请求处理过程中抛出的异常将被应用程序上下文中声明的任何端点异常解析器捕获。使用这些异常解析器,您可以定义自定义行为(例如返回 SOAP 错误),以防抛出此类异常。
MessageDispatcher
具有多个属性,用于设置端点适配器、映射、异常解析器。但是,设置这些属性不是必需的,因为分派器将自动检测在应用程序上下文中注册的所有这些类型。仅当需要覆盖检测时,才应设置这些属性。
消息分派器在 消息上下文上运行,而不是特定于传输的输入流和输出流。因此,特定于传输的请求需要读取到 MessageContext
中。对于 HTTP,这是通过 WebServiceMessageReceiverHandlerAdapter
完成的,它是一个 Spring Web HandlerInterceptor
,以便可以在标准 DispatcherServlet
中连接 MessageDispatcher
。但是,有一种更方便的方法可以做到这一点,如第 5.3.1 节“MessageDispatcherServlet
”所示。
Spring Web 服务支持多种传输协议。最常见的是 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 服务的第一步,因为 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 服务 bean,例如端点、编组器等。
MessageDispatcherServlet
将自动检测在其 Spring 容器中定义的任何 WsdlDefinition
bean。所有检测到的此类 WsdlDefinition
bean 也将通过 WsdlDefinitionHandlerAdapter
公开;这是一种非常方便的方法,只需定义一些 bean 即可将 WSDL 公开给客户端。
例如,考虑以下 <static-wsdl>
定义,该定义在 Spring-WS 配置文件(/WEB-INF/[servlet-name]-servlet.xml
)中定义。请注意 'id
' 属性的值,因为这将在公开 WSDL 时使用。
<sws:static-wsdl id="orders" location="/WEB-INF/wsdl/orders.wsdl"/>
然后可以通过以下格式的 URL 的 GET
请求访问 'Orders.wsdl
' 文件中定义的 WSDL(根据需要替换主机、端口和 servlet 上下文路径)。
https://127.0.0.1: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://127.0.0.1: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
端口类型中。
如果要通过 include 或 import 使用多个模式,则需要将 Commons XMLSchema 放入类路径中。如果 Commons XMLSchema 位于类路径中,则上面的 <dynamic-wsdl>
元素将遵循所有 XSD import 和 include,并将它们作为单个 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://127.0.0.1?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
)。您只需要定义服务器的实例并将其配置为处理传入请求。核心 Spring 框架中的远程处理模块包含一个方便的 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 的支持与其他传输非常相似:有一个 XmppMessageSender
用于 WebServiceTemplate
,以及一个 XmppMessageReceiver
用于与 MessageDispatcher
一起使用。
以下示例显示了如何设置服务器端 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 | 任何布尔值、双精度浮点数、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 支持的任何类型。 | ✓ | 当 unmarshaller 属性在 <sws:annotation-driven/> 中指定时启用。 |
以下是一些可能的处理方法签名的示例
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
String
Node
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 支持的任何类型。 | ✓ | 当 marshaller 属性在 <sws:annotation-driven/> 中指定时启用。 |
如您所见,在定义处理方法签名时有很多可能性。甚至可以扩展此机制,并支持您自己的参数类型。请参阅 DefaultMethodEndpointAdapter
和 MethodReturnValueHandler
的类级 Javadoc 以了解如何操作。
端点映射负责将传入的消息映射到相应的端点。有一些端点映射默认启用,例如 PayloadRootAnnotationMethodEndpointMapping
或 SoapActionAnnotationMethodEndpointMapping
,但让我们首先检查 EndpointMapping
的一般概念。
EndpointMapping
提供一个 EndpointInvocationChain
,其中包含与传入请求匹配的端点,并且可能还包含将应用于请求和响应的端点拦截器列表。当请求进来时,MessageDispatcher
将将其交给端点映射,让它检查请求并生成合适的 EndpointInvocationChain
。然后,MessageDispatcher
将调用端点和链中的任何拦截器。
可配置的端点映射的概念可以包含可选的拦截器(可以操作请求或响应,或两者兼而有之),这非常强大。许多支持功能可以构建到自定义 EndpointMapping
中。例如,可能存在一个自定义端点映射,它不仅根据消息内容选择端点,还根据特定的 SOAP 标头(或多个 SOAP 标头)选择端点。
大多数端点映射继承自 AbstractEndpointMapping
,它提供了一个“interceptors”属性,该属性是使用的拦截器列表。EndpointInterceptors
在 第 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
时,响应将不会发送回客户端。
您可以使用许多标准的 EndpointInterceptor
实现。此外,还有 XwsSecurityInterceptor
,它在 第 7.2 节,“XwsSecurityInterceptor
” 中进行了描述。
在开发 Web 服务时,记录传入和传出的 XML 消息可能很有用。SWS 通过 PayloadLoggingInterceptor
和 SoapEnvelopeLoggingInterceptor
类来促进此操作。前者仅将消息的有效负载记录到 Commons Logging 日志;后者记录整个 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 服务非常严格。通常,请求是否有效并不重要,重要的是端点能否获得足够的信息来满足请求。验证响应确实是一个好主意,因为端点应遵守其模式。请记住 Postel 定律:““在您所做的事情上要保守;在您接受他人的事情上要宽容。”
以下是一个使用 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 服务器或 SOAP 1.2 接收器故障,并使用异常消息作为故障字符串。 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
类型的异常映射到具有故障字符串 "Invalid request"
的客户端 SOAP 故障,如下面的响应所示
<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); } }
每当在端点调用期间抛出 MyBusinessException
并使用构造函数字符串 "Oops!"
时,都会导致以下响应
<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框架中提供的标准测试工具。这不是必需的,但通常是设置测试的最简单方法。 | |
应用程序上下文是一个标准的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。