构成 Spring Web 服务的各种组件

时间:2022-12-20 16:05:06

构成 Spring Web 服务的各种组件

参考文档的这一部分详细介绍了构成 Spring Web 服务的各种组件。这包括一章讨论客户端和服务器端 WS 共有的部分,一章​专门介绍编写服务器端​ Web 服务的细节,一章关于在客户端​使用 Web 服务,以及一章关于使用 WS-Security。

4. 共享组件

本章探讨了客户端和服务器端 Spring-WS 开发之间共享的组件。这些接口和类代表了 Spring-WS 的构建块,因此即使你不直接使用它们,你也需要了解它们的作用。

4.1. 网络服务消息

本节介绍 Spring-WS 使用的消息和消息工厂。

4.1.1.​​WebServiceMessage​

Spring Web Services 的核心接口之一是 .此接口表示与协议无关的 XML 消息。该接口包含以 a 或 a 的形式提供对消息有效负载的访问的方法。 并且是标记表示 XML 输入和输出抽象的接口。具体的实现包装了各种 XML 表示形式,如下表所示:​​WebServiceMessage​​​​javax.xml.transform.Source​​​​javax.xml.transform.Result​​​​Source​​​​Result​

源或结果实现

包装的 XML 表示形式

​javax.xml.transform.dom.DOMSource​

​org.w3c.dom.Node​

​javax.xml.transform.dom.DOMResult​

​org.w3c.dom.Node​

​javax.xml.transform.sax.SAXSource​

​org.xml.sax.InputSource​​​和​​org.xml.sax.XMLReader​

​javax.xml.transform.sax.SAXResult​

​org.xml.sax.ContentHandler​

​javax.xml.transform.stream.StreamSource​

​java.io.File​​​或​​java.io.InputStream​​​​java.io.Reader​

​javax.xml.transform.stream.StreamResult​

​java.io.File​​​或​​java.io.OutputStream​​​​java.io.Writer​

除了读取和写入有效负载之外,Web 服务消息还可以将自身写入输出流。

4.1.2.​​SoapMessage​

​SoapMessage​​是 的子类。它包含特定于 SOAP 的方法,例如获取 SOAP 标头、SOAP 错误等。通常,代码不应依赖于 ,因为可以通过在 中使用 和 获取 SOAP 正文的内容(消息的有效负载)。仅当需要执行特定于 SOAP 的操作(如添加标头、获取附件等)时,才需要强制转换为 。​​WebServiceMessage​​​​SoapMessage​​​​getPayloadSource()​​​​getPayloadResult()​​​​WebServiceMessage​​​​WebServiceMessage​​​​SoapMessage​

4.1.3. 消息工厂

具体的消息实现由 .此工厂可以创建空消息或从输入流中读取消息。有两个具体的实现。一个是基于SAAJ,即Java的带有附件API的SOAP。另一个基于 Axis 2 的 AXIOM(AXis 对象模型)。​​WebServiceMessageFactory​​​​WebServiceMessageFactory​

​SaajSoapMessageFactory​

它使用 SOAP with Attachments API for Java (SAAJ) 来创建实现。SAAJ 是 J2EE 1.4 的一部分,因此在大多数现代应用程序服务器下都应该支持它。以下是常见应用程序服务器提供的 SAAJ 版本的概述:​​SaajSoapMessageFactory​​​​SoapMessage​

应用服务器

SAAJ 版本

BEA WebLogic 8

1.1

BEA WebLogic 9

1.1/1.21

IBM WebSphere 6

1.2

太阳玻璃鱼 1

1.3

1Weblogic 9 在 SAAJ 1.2 实现中有一个已知的错误:它实现了所有 1.2 接口,但在调用时抛出一个。Spring Web Services 有一个解决方法:它在 WebLogic 9 上运行时使用 SAAJ 1.1。​​UnsupportedOperationException​

此外,Java SE 6 还包括 SAAJ 1.3。您可以按如下方式连接:​​SaajSoapMessageFactory​

<bean  class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory" />

SAAJ 基于 DOM,即文档对象模型。这意味着所有 SOAP 消息都存储在内存中。对于较大的 SOAP 消息,这可能不是性能。在这种情况下,可能更适用。​​AxiomSoapMessageFactory​

​AxiomSoapMessageFactory​

使用 AXis 2 对象模型 (AXIOM) 创建实现。AXIOM 基于 StAX,即 XML 的流式处理 API。StAX 提供了一种基于拉取的机制来读取 XML 消息,这对于较大的消息可能更有效。​​AxiomSoapMessageFactory​​​​SoapMessage​

若要提高 的读取性能,可以将该属性设置为 false(默认值为 true)。这样做会导致直接从套接字流中读取 SOAP 正文的内容。启用此设置后,有效负载只能读取一次。这意味着您必须确保消息的任何预处理(日志记录或其他工作)都不会使用它。​​AxiomSoapMessageFactory​​​​payloadCaching​

您可以按如下方式使用:​​AxiomSoapMessageFactory​

<bean  class="org.springframework.ws.soap.axiom.AxiomSoapMessageFactory">
<property name="payloadCaching" value="true"/>
</bean>

除了有效负载缓存之外,AXIOM 还支持完整的流消息,如 中所定义。这意味着您可以直接在响应消息上设置有效负载,而不是将其写入 DOM 树或缓冲区。​​StreamingWebServiceMessage​

当处理程序方法返回 JAXB2 支持的对象时,将使用 AXIOM 的完整流。它会自动将此封送对象设置为响应消息,并在响应发出时将其写出到传出套接字流。

有关完整流式处理的更多信息,请参阅 和 的类级 Javadoc。​​StreamingWebServiceMessage​​​​StreamingPayload​

肥皂 1.1 或 1.2

和 都有一个属性,您可以在其中注入一个常量。默认情况下,版本为 1.1,但您可以将其设置为 1.2:​​SaajSoapMessageFactory​​​​AxiomSoapMessageFactory​​​​soapVersion​​​​SoapVersion​

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd">

<bean class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory">
<property name="soapVersion">
<util:constant static-field="org.springframework.ws.soap.SoapVersion.SOAP_12"/>
</property>
</bean>

</beans>

在前面的示例中,我们定义了一个仅接受 SOAP 1.2 消息的 。​​SaajSoapMessageFactory​


尽管两个版本的 SOAP 在格式上非常相似,但 1.2 版本与 1.1 不向后兼容,因为它使用不同的 XML 命名空间。SOAP 1.1 和 1.2 之间的其他主要区别包括错误的不同结构以及 HTTP 标头实际上已被弃用的事实,尽管它们仍然有效。​​SOAPAction​



对于 SOAP 版本号(或一般的 WS-* 规范版本号),需要注意的重要一点是,规范的最新版本通常不是最受欢迎的版本。对于 SOAP,这意味着(目前)使用的最佳版本是 1.1。1.2版本将来可能会变得更流行,但1.1是目前最安全的选择。


4.1.4.​​MessageContext​

通常,消息成对出现:请求和响应。在客户端创建一个请求,该请求通过某种传输发送到服务器端,在那里生成响应。此响应将发送回客户端,并在客户端读取。

在 Spring Web 服务中,这样的对话包含在 中,该 具有获取请求和响应消息的属性。在客户端,消息上下文由 Web 服务模板创建。在服务器端,从特定于传输的输入流中读取消息上下文。例如,在 HTTP 中,它从 读取,并将响应写回 。​​MessageContext​​​​HttpServletRequest​​​​HttpServletResponse​

4.2.​​TransportContext​

SOAP 协议的一个关键属性是它尝试与传输无关。这就是为什么,例如,Spring-WS不支持通过HTTP请求URL而是通过消息内容将消息映射到端点。

但是,有时需要在客户端或服务器端访问基础传输。为此,Spring Web Services 具有 .传输上下文允许访问底层,通常是在服务器端或或客户端。例如,可以在服务器端终结点或侦听器中获取当前请求的 IP 地址:​​TransportContext​​​​WebServiceConnection​​​​HttpServletConnection​​​​HttpUrlConnection​​​​CommonsHttpConnection​

TransportContext context = TransportContextHolder.getTransportContext();
HttpServletConnection connection = (HttpServletConnection )context.getConnection();
HttpServletRequest request = connection.getHttpServletRequest();
String ipAddress = request.getRemoteAddr();

4.3. 使用 XPath 处理 XML

处理 XML 的最佳方法之一是使用 XPath。引述[有效-xml],第35项:

XPath 是第四代声明性语言,它允许您指定要处理的节点,而无需准确指定处理器应如何导航到这些节点。XPath的数据模型设计得非常好,可以支持几乎所有开发人员都希望从XML中获得的东西。例如,它合并所有相邻的文本,包括 CDATA 部分中的文本,允许计算跳过注释和处理指令的值,并包含来自子元素和后代元素的文本,并要求解析所有外部实体引用。在实践中,XPath 表达式对于输入文档中的意外但可能微不足道的更改往往更加可靠。

— 艾略特·鲁斯蒂·哈罗德

Spring Web Services 有两种方法可以在应用程序中使用 XPath:更快或更灵活。​​XPathExpression​​​​XPathTemplate​

4.3.1.​​XPathExpression​

它是对已编译的 XPath 表达式(如 Java 5 接口或 Jaxen 类)的抽象。要在应用程序上下文中构造表达式,可以使用 。以下示例使用此工厂 Bean:​​XPathExpression​​​​javax.xml.xpath.XPathExpression​​​​XPath​​​​XPathExpressionFactoryBean​

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

<bean class="org.springframework.xml.xpath.XPathExpressionFactoryBean">
<property name="expression" value="/Contacts/Contact/Name"/>
</bean>

<bean class="sample.MyXPathClass">
<constructor-arg ref="nameExpression"/>
</bean>

</beans>

前面的表达式不使用命名空间,但我们可以使用工厂 bean 的属性来设置这些命名空间。该表达式可以在代码中使用,如下所示:​​namespaces​

package sample;

public class MyXPathClass {

private final XPathExpression nameExpression;

public MyXPathClass(XPathExpression nameExpression) {
this.nameExpression = nameExpression;
}

public void doXPath(Document document) {
String name = nameExpression.evaluateAsString(document.getDocumentElement());
System.out.println("Name: " + name);
}

}

对于更灵活的方法,您可以使用 ,它类似于 Spring 的 JDBC 支持。以下示例演示如何使用它:​​NodeMapper​​​​RowMapper​

package sample;

public class MyXPathClass {

private final XPathExpression contactExpression;

public MyXPathClass(XPathExpression contactExpression) {
this.contactExpression = contactExpression;
}

public void doXPath(Document document) {
List contacts = contactExpression.evaluate(document,
new NodeMapper() {
public Object mapNode(Node node, int nodeNum) throws DOMException {
Element contactElement = (Element) node;
Element nameElement = (Element) contactElement.getElementsByTagName("Name").item(0);
Element phoneElement = (Element) contactElement.getElementsByTagName("Phone").item(0);
return new Contact(nameElement.getTextContent(), phoneElement.getTextContent());
}
});
PlainText Section qName; // do something with the list of Contact objects
}
}

与Spring JDBC中的映射行类似,每个结果节点都使用匿名内部类进行映射。在这种情况下,我们创建一个对象,稍后会使用它。​​RowMapper​​​​Contact​

4.3.2.​​XPathTemplate​

允许您仅计算单个预编译表达式。更灵活但速度较慢的替代方法是 .此类遵循整个 Spring 中使用的通用模板模式(、 和其他)。下面的清单显示了一个示例:​​XPathExpression​​​​XpathTemplate​​​​JdbcTemplate​​​​JmsTemplate​

package sample;

public class MyXPathClass {

private XPathOperations template = new Jaxp13XPathTemplate();

public void doXPath(Source source) {
String name = template.evaluateAsString("/Contacts/Contact/Name", request);
// do something with name
}

}

4.4. 消息记录和跟踪

在开发或调试 Web 服务时,在 (SOAP) 消息到达时或发送之前查看其内容非常有用。 Spring Web Services 通过标准的 Commons Logging 接口提供此功能。

确保使用共享资源日志记录版本1.1或更高版本。早期版本存在类加载问题,并且不与 Log4J TRACE 级别集成。

要记录所有服务器端消息,请将记录器级别设置为 或 。在级别上,仅记录有效负载根元素。在级别上,将记录整个消息内容。如果只想记录已发送的消息,请使用记录器。同样,您可以使用 仅记录收到的消息。​​org.springframework.ws.server.MessageTracing​​​​DEBUG​​​​TRACE​​​​DEBUG​​​​TRACE​​​​org.springframework.ws.server.MessageTracing.sent​​​​org.springframework.ws.server.MessageTracing.received​

在客户端,存在类似的记录器:和 。​​org.springframework.ws.client.MessageTracing.sent​​​​org.springframework.ws.client.MessageTracing.received​

以下示例的配置文件记录客户端上已发送消息的完整内容,并且仅记录客户端接收消息的有效负载根元素。在服务器端,为发送和接收的消息记录有效负载根:​​log4j.properties​

log4j.rootCategory=INFO, stdout
log4j.logger.org.springframework.ws.client.MessageTracing.sent=TRACE
log4j.logger.org.springframework.ws.client.MessageTracing.received=DEBUG

log4j.logger.org.springframework.ws.server.MessageTracing=DEBUG

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%p [%c{3}] %m%n

使用此配置时,典型输出为:

TRACE [client.MessageTracing.sent] Sent request [<SOAP-ENV:Envelope xmlns:SOAP-ENV="...
DEBUG [server.MessageTracing.received] Received request [SaajSoapMessage {http://example.com}request] ...
DEBUG [server.MessageTracing.sent] Sent response [SaajSoapMessage {http://example.com}response] ...
DEBUG [client.MessageTracing.received] Received response [SaajSoapMessage {http://example.com}response] ...

5. 使用 Spring-WS 创建一个 Web 服务

Spring-WS 的服务器端支持是围绕将传入消息调度到端点而设计的,具有可配置的端点映射、响应生成和端点拦截。终结点通常使用注释进行批注,并具有一种或多种处理方法。这些方法通过检查消息的各个部分(通常是有效负载)来处理传入的 XML 请求消息,并创建某种响应。您可以使用另一个注释(通常是 )来批注该方法,以指示它可以处理的消息类型。​​MessageDispatcher​​​​@Endpoint​​​​@PayloadRoot​

Spring-WS的XML处理非常灵活。端点可以从 Spring-WS 支持的大量 XML 处理库中进行选择,包括:

  • DOM 家族:W3C DOM、JDOM、dom4j 和 XOM
  • SAX 或 StAX:实现更快的性能
  • XPath:从邮件中提取信息
  • 编组技术(JAXB、Castor、XMLBeans、JiBX 或 XStream):将 XML 转换为对象,反之亦然

5.1.​​MessageDispatcher​

Spring-WS 的服务器端是围绕一个中心类设计的,该中心类将传入的 XML 消息调度到端点。Spring-WS非常灵活,允许你使用任何类型的类作为端点,只要它可以在Spring IoC容器中配置。在某种程度上,消息调度程序类似于 Spring 的 , Spring Web MVC 中使用的“前端控制器”。​​MessageDispatcher​​​​DispatcherServlet​

以下序列图显示了 的处理和调度流程:​​MessageDispatcher​

当 设置为使用并且收到针对该特定调度程序的请求时,将开始处理该请求。以下过程描述了如何处理请求:​​MessageDispatcher​​​​MessageDispatcher​​​​MessageDispatcher​

  1. 将搜索配置的终结点以查找适当的终结点。如果找到终结点,则会调用与终结点关联的调用链(预处理器、后处理器和终结点)以创建响应。EndpointMapping(s)
  2. 为端点找到合适的适配器。委托到此适配器以调用端点。MessageDispatcher
  3. 如果返回响应,则会在途中发送响应。如果未返回任何响应(这可能是由于前处理器或后处理器截获了请求,例如,出于安全原因),则不会发送响应。

在处理请求期间引发的异常将由在应用程序上下文中声明的任何终结点异常解析程序选取。使用这些异常解析程序可以定义自定义行为(例如返回 SOAP 错误),以防引发此类异常。

具有多个用于设置端点适配器、映射、异常解析程序的属性。但是,不需要设置这些属性,因为调度程序会自动检测在应用程序上下文中注册的所有类型。仅当需要替代检测时,才应设置这些属性。MessageDispatcher

消息分派器对消息上下文进行操作,而不是对特定于传输的输入流和输出流进行操作。因此,特定于传输的请求需要读入 .对于HTTP,这是用一个(这是一个Spring Web)完成的,这样就可以在标准中连接。但是,有一种更方便的方法可以做到这一点,它显示在 MessageDispatcherServlet 中。​​MessageContext​​​​WebServiceMessageReceiverHandlerAdapter​​​​HandlerInterceptor​​​​MessageDispatcher​​​​DispatcherServlet​

5.2. 运输

Spring Web Services 支持多种传输协议。最常见的是HTTP传输,它提供了自定义servlet,但您也可以通过JMS甚至电子邮件发送消息。

5.2.1.​​MessageDispatcherServlet​

这是一个标准,它方便地从标准 Spring Web 扩展并包装一个 .因此,它将这些属性合二为一。作为 ,它遵循与上一节中所述相同的请求处理流程。作为 servlet,在 Web 应用程序中配置。要处理的请求必须由同一文件中的 URL 映射映射。这是标准的 Java EE servlet 配置。下面的示例演示了此类声明和映射:​​MessageDispatcherServlet​​​​Servlet​​​​DispatcherServlet​​​​MessageDispatcher​​​​MessageDispatcher​​​​MessageDispatcherServlet​​​​web.xml​​​​MessageDispatcherServlet​​​​web.xml​​​​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 Web 服务的第一步,因为 Spring-WS 框架使用的各种组件 bean 也需要配置。此配置由标准的 Spring XML 定义组成。因为是标准的弹簧,所以 在 Web 应用程序的目录中查找一个名为 [servlet-name]-servlet.xml 的文件,并在 Spring 容器中创建在其中定义的 bean。在前面的示例中,它查找“/WEB-INF/spring-ws-servlet.xml”。此文件包含所有 Spring Web Services bean,例如端点、编组程序等。​​spring-ws​​​​MessageDispatcherServlet​​​​<bean/>​​​​MessageDispatcherServlet​​​​DispatcherServlet​​​​WEB-INF​

作为替代方法,如果您在 Servlet 3+ 环境中运行,则可以以编程方式配置 Spring-WS。为此,Spring-WS提供了许多抽象基类,这些基类扩展了Spring Framework中的接口。如果还对 Bean 定义使用类,则应扩展 :​​web.xml​​​​WebApplicationInitializer​​​​@Configuration​​​​AbstractAnnotationConfigMessageDispatcherServletInitializer​

public class MyServletInitializer
extends AbstractAnnotationConfigMessageDispatcherServletInitializer {

@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{MyRootConfig.class};
}

@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{MyEndpointConfig.class};
}

}

在前面的示例中,我们告诉 Spring 可以在类(类)中找到端点 Bean 定义。其他 Bean 定义(通常是服务、存储库等)可以在类中找到。缺省情况下,会将 servlet 映射到两种模式:和 ,尽管您可以通过重写该方法来更改此模式。有关 的编程配置的更多详细信息,请参阅 AbstractMessageDispatcherServletInitializer 和 AbstractAnnotationConfigMessageDispatcherServletInitializer 的 Javadoc。​​MyEndpointConfig​​​​@Configuration​​​​MyRootConfig​​​​AbstractAnnotationConfigMessageDispatcherServletInitializer​​​​/services​​​​*.wsdl​​​​getServletMappings()​​​​MessageDispatcherServlet​

自动公开 WSDL

自动检测其 Spring 容器中定义的任何 bean。所有检测到的豆子也通过 .这是通过定义一些 bean 向客户端公开 WSDL 的便捷方法。​​MessageDispatcherServlet​​​​WsdlDefinition​​​​WsdlDefinition​​​​WsdlDefinitionHandlerAdapter​

举个例子,考虑以下定义,定义在 Spring-WS 配置文件 ()。请注意属性的值,因为它是在公开 WSDL 时使用的。​​<static-wsdl>​​​​/WEB-INF/[servlet-name]-servlet.xml​​​​id​

<sws:static-wsdl  location="orders.wsdl"/>

或者,它可以是类中的方法:​​@Bean​​​​@Configuration​

@Bean
public SimpleWsdl11Definition orders() {
return new SimpleWsdl11Definition(new ClassPathResource("orders.wsdl"));
}

您可以通过请求以下形式的 URL 来访问类路径上文件中定义的 WSDL(根据需要替换主机、端口和 servlet 上下文路径):​​orders.wsdl​​​​GET​

http://localhost:8080/spring-ws/orders.wsdl

所有 Bean 定义都由其 Bean 名称下的 公开,后缀为 '.wsdl'。因此,如果 Bean 名称为 ,主机名为 ,并且 Servlet 上下文(战争名称)为 ,则可以在 中找到 WSDL。​​WsdlDefinition​​​​MessageDispatcherServlet​​​​echo​​​​server​​​​spring-ws​​​​http://server/spring-ws/echo.wsdl​

(或更准确地说是)的另一个不错的功能是,它可以转换它公开的所有 WSDL 的值,以反映传入请求的 URL。​​MessageDispatcherServlet​​​​WsdlDefinitionHandlerAdapter​​​​location​

请注意,默认情况下,此转换功能处于关闭状态。要打开此功能,您需要将初始化参数指定为 :​​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>

如果使用 ,则启用转换就像重写方法以返回 一样简单。​​AbstractAnnotationConfigMessageDispatcherServletInitializer​​​​isTransformWsdlLocations()​​​​true​

请参阅 WsdlDefinitionHandlerAdapter 类上的类级 Javadoc,以了解有关整个转换过程的更多信息。

作为手动编写WSDL并使用 公开它的替代方法,Spring Web Services也可以从XSD模式生成WSDL。这是发布 WSDL 中所示的方法。下一个应用程序上下文代码段演示如何创建这样的动态 WSDL 文件:​​<static-wsdl>​

<sws:dynamic-wsdl 
portTypeName="Orders"
locationUri="http://localhost:8080/ordersService/">
<sws:xsd location="Orders.xsd"/>
</sws:dynamic-wsdl>

或者,您可以使用 Java 方法:​​@Bean​

@Bean
public DefaultWsdl11Definition orders() {
DefaultWsdl11Definition definition = new DefaultWsdl11Definition();
definition.setPortTypeName("Orders");
definition.setLocationUri("http://localhost:8080/ordersService/");
definition.setSchema(new SimpleXsdSchema(new ClassPathResource("echo.xsd")));

return definition;
}

该元素取决于类。此定义类使用 org.springframework.ws.wsdl.wsdl11.provider 包中的 WSDL 提供程序和 ProviderBasedWsdl4jDefinition 类在第一次请求时生成 WSDL。如有必要,请参阅这些类的类级 Javadoc,了解如何扩展此机制。​​<dynamic-wsdl>​​​​DefaultWsdl11Definition​

该(因此,标记)使用约定从 XSD 架构生成 WSDL。它循环访问架构中找到的所有元素,并为所有元素创建一个。接下来,它为所有以定义的请求或响应后缀结尾的消息创建一个 WSDL。缺省请求后缀为 。默认响应后缀是 ,但可以通过分别在 上设置 和 属性来更改这些后缀。它还基于操作构建 、 和。​​DefaultWsdl11Definition​​​​<dynamic-wsdl>​​​​element​​​​message​​​​operation​​​​Request​​​​Response​​​​requestSuffix​​​​responseSuffix​​​​<dynamic-wsdl />​​​​portType​​​​binding​​​​service​

例如,如果我们的架构定义了 and 元素,则会创建一个 and 消息和一个操作,该操作放在端口类型中。​​Orders.xsd​​​​GetOrdersRequest​​​​GetOrdersResponse​​​​<dynamic-wsdl>​​​​GetOrdersRequest​​​​GetOrdersResponse​​​​GetOrders​​​​Orders​

要使用多个模式,无论是通过包含还是导入,你可以将Commons XMLSchema放在类路径上。如果 Commons XMLSchema 位于类路径上,则该元素遵循所有 XSD 导入,并将它们作为单个 XSD 包含在 WSDL 中。这大大简化了架构的部署,同时仍然可以单独编辑它们。​​<dynamic-wsdl>​

尽管在运行时从 XSD 创建 WSDL 可能很方便,但这种方法也存在一些缺点。首先,尽管我们试图在版本之间保持 WSDL 生成过程的一致性,但它仍有可能(略有变化)。其次,生成速度有点慢,但是,一旦生成,WSDL 就会被缓存以供以后参考。

因此,应仅在项目的开发阶段使用。我们建议使用浏览器下载生成的 WSDL,将其存储在项目中,并使用 .这是真正确保 WSDL 不会随时间变化的唯一方法。​​<dynamic-wsdl>​​​​<static-wsdl>​

5.2.2. 在​​DispatcherServlet​

作为 的替代方案,您可以在标准的 Spring-Web MVC 中连接一个。默认情况下,只能委托给 ,但我们可以通过向 servlet 的 Web 应用程序上下文添加 a 来指示它委托给 a:​​MessageDispatcherServlet​​​​MessageDispatcher​​​​DispatcherServlet​​​​DispatcherServlet​​​​Controllers​​​​MessageDispatcher​​​​WebServiceMessageReceiverHandlerAdapter​

<beans>

<bean class="org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter"/>

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="defaultHandler" ref="messageDispatcher"/>
</bean

<bean class="org.springframework.ws.soap.server.SoapMessageDispatcher"/>

...

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>

</beans>

请注意,通过显式添加 ,调度程序 servlet 不会加载缺省适配器,并且无法处理标准的 Spring-MVC 。因此,我们在末尾添加。​​WebServiceMessageReceiverHandlerAdapter​​​​@Controllers​​​​RequestMappingHandlerAdapter​

以类似的方式,您可以连接 a 以确保可以处理接口的实现:​​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 class="org.springframework.ws.soap.server.SoapMessageDispatcher"/>

<bean class="org.springframework.ws.wsdl.wsdl11.SimpleWsdl11Definition">
<prop name="wsdl" value="/WEB-INF/myServiceDefintion.wsdl"/>
</bean>

...

</beans>

5.2.3. JMS 传输

Spring Web Services 通过 Spring 框架中提供的 JMS 功能支持服务器端 JMS 处理。Spring Web Services 提供了插入 .此消息侦听器需要 和 才能操作。以下配置示例显示了这一点:​​WebServiceMessageListener​​​​MessageListenerContainer​​​​WebServiceMessageFactory​​​​MessageDispatcher​

<beans>

<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="vm://localhost?broker.persistent=false"/>
</bean>

<bean 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 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>

5.2.4. 电子邮件传输

除了HTTP和JMS,Spring Web Services还提供服务器端电子邮件处理。此功能通过类提供。此类监视 POP3 或 IMAP 文件夹,将电子邮件转换为 ,并使用 SMTP 发送任何响应。可以通过 来配置主机名,前者指示要监视请求的邮件文件夹(通常为 POP3 或 IMAP 文件夹),后者指示用于发送响应的服务器(通常为 SMTP 服务器)。​​MailMessageReceiver​​​​WebServiceMessage​​​​storeUri​​​​transportUri​

您可以使用可插拔策略配置 如何监视传入消息:.默认情况下,使用轮询策略,其中传入文件夹每五分钟轮询一次新邮件。您可以通过设置策略的属性来更改此间隔。默认情况下,所有实现都会删除已处理的消息。可以通过设置属性来更改此设置。​​MailMessageReceiver​​​​MonitoringStrategy​​​​pollingInterval​​​​MonitoringStrategy​​​​deleteMessages​

作为效率非常低的轮询方法的替代方法,有一种使用 IMAP IDLE 的监视策略。IDLE 命令是 IMAP 电子邮件协议的可选扩展,它允许邮件服务器以异步方式向 发送新邮件更新。如果使用支持 IDLE 命令的 IMAP 服务器,则可以将 插入到属性中。除了支持服务器之外,您还需要使用 JavaMail 版本 1.4.1 或更高版本。​​MailMessageReceiver​​​​ImapIdleMonitoringStrategy​​​​monitoringStrategy​

以下配置显示了如何使用服务器端电子邮件支持,覆盖默认轮询间隔以每 30 秒(30.000 毫秒)检查一次:

<beans>

<bean class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>

<bean class="org.springframework.ws.transport.mail.MailMessageReceiver">
<property name="messageFactory" ref="messageFactory"/>
<property name="from" value="Spring-WS SOAP Server <server@example.com>"/>
<property name="storeUri" value="imap://server:s04p@imap.example.com/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 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>

5.2.5. 嵌入式 HTTP 服务器传输

Spring Web Services 提供基于 Sun 的 JRE 1.6 HTTP 服务器的传输。嵌入式 HTTP 服务器是一个易于配置的独立服务器。它提供了传统 servlet 容器的更轻的替代品。

使用嵌入式 HTTP 服务器时,不需要外部部署描述符 ()。您只需定义服务器的实例并将其配置为处理传入请求。Core Spring 框架中的远程处理模块包含一个方便的 HTTP 服务器工厂 bean:.最重要的属性是 ,它将上下文路径映射到相应的实例。​​web.xml​​​​SimpleHttpServerFactoryBean​​​​contexts​​​​HttpHandler​

Spring Web Services 提供了两种接口实现:WsdlDefinitionHttpHandler 和 WebServiceMessageReceiverHttpHandler。前者将传入的GET请求映射到.后者负责处理Web服务消息的POST请求,因此需要a(通常是a)和a(通常是)来完成其任务。​​HttpHandler​​​​WsdlDefinition​​​​WebServiceMessageFactory​​​​SaajSoapMessageFactory​​​​WebServiceMessageReceiver​​​​SoapMessageDispatcher​

为了与 servlet 世界相提并论,该属性在 中扮演 servlet 映射的角色,并且等效于 .​​contexts​​​​web.xml​​​​WebServiceMessageReceiverHttpHandler​​​​MessageDispatcherServlet​

以下代码片段显示了 HTTP 服务器传输的配置示例:

<beans>

<bean class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>

<bean class="org.springframework.ws.soap.server.SoapMessageDispatcher">
<property name="endpointMappings" ref="endpointMapping"/>
</bean>

<bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
<property name="defaultEndpoint" ref="stockEndpoint"/>
</bean>

<bean 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 class="org.springframework.ws.transport.http.WebServiceMessageReceiverHttpHandler">
<property name="messageFactory" ref="messageFactory"/>
<property name="messageReceiver" ref="messageReceiver"/>
</bean>

<bean class="org.springframework.ws.transport.http.WsdlDefinitionHttpHandler">
<property name="definition" ref="wsdlDefinition"/>
</bean>
</beans>

有关 的更多信息,请参阅 Javadoc。​​SimpleHttpServerFactoryBean​

5.2.6. XMPP 传输

Spring Web Services 2.0引入了对XMPP的支持,也称为Jabber。该支持基于 Smack 库。

Spring Web Services 对 XMPP 的支持与其他传输非常相似:有一个 a for 和 a 用于 .​​XmppMessageSender​​​​WebServiceTemplate​​​​XmppMessageReceiver​​​​MessageDispatcher​

下面的示例演示如何设置服务器端 XMPP 组件:

<beans>

<bean class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>

<bean 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 class="org.springframework.ws.transport.xmpp.XmppMessageReceiver">
<property name="messageFactory" ref="messageFactory"/>
<property name="connection" ref="connection"/>
<property name="messageReceiver" ref="messageDispatcher"/>
</bean>

<bean 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>

5.2.7. MTOM

MTOM 是用于向 Web 服务发送二进制数据或从 Web 服务发送二进制数据的机制。您可以通过 MTOM 示例了解如何使用 Spring WS 实现这一点。

5.3. 端点

端点是 Spring-WS 服务器端支持的核心概念。终结点提供对应用程序行为的访问,该行为通常由业务服务接口定义。终结点解释 XML 请求消息,并使用该输入(通常)调用业务服务上的方法。该服务调用的结果表示为响应消息。Spring-WS具有各种各样的端点,并使用各种方法来处理XML消息并创建响应。

可以通过使用注释批注类来创建终结点。在该类中,通过使用各种参数类型(如 DOM 元素、JAXB2 对象等)定义一个或多个处理传入 XML 请求的方法。可以使用其他批注(通常)来指示方法可以处理的消息类型。​​@Endpoint​​​​@PayloadRoot​

请考虑以下示例终结点:

package samples;

@Endpoint
public 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());
}

...

}

该类用 注释,将其标记为 Spring-WS 端点。​​@Endpoint​

构造函数标记为 ,以便将业务服务注入到此终结点中。​​@Autowired​​​​OrderService​

该方法采用 (用 ) 批注 )作为参数。这意味着消息的有效负载作为 DOM 元素在此方法上传递。该方法具有返回类型,指示不发送响应消息。 有关终端节点方法的更多信息,请参阅@Endpoint处理方法​。​​order​​​​Element​​​​@RequestPayload​​​​void​

该方法采用 (也用 ) 作为参数。此参数是支持 JAXB2 的对象(用 注释)。这意味着消息的有效负载将作为未编组的对象传递给此方法。类型也作为参数给出。调用时,此参数包含请求消息的 SOAP 标头。该方法还用 注释,指示返回值 (the ) 用作响应消息的有效负载。 有关终端节点方法的更多信息,请参阅@Endpoint处理方法​。​​getOrder​​​​OrderRequest​​​​@RequestPayload​​​​@XmlRootElement​​​​SoapHeader​​​​@ResponsePayload​​​​Order​

此端点的两种处理方法都用 标记,指示该方法可以处理哪种请求消息:该方法对具有本地名称和命名空间 URI 的请求调用该方法。对于具有本地名称的请求,将调用 order 方法。 有关 的详细信息,请参阅终结点映射​。​​@PayloadRoot​​​​getOrder​​​​orderRequest​​​​http://samples​​​​order​​​​@PayloadRoot​

要启用对 Spring-WS 注释的支持和相关注释,您需要将以下内容添加到 Spring 应用程序上下文中:​​@Endpoint​

<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.xsd
http://www.springframework.org/schema/web-services
http://www.springframework.org/schema/web-services/web-services.xsd">

*<sws:annotation-driven />

</beans>

或者,如果您使用类而不是Spring XML,则可以使用以下方法注释配置类:​​@Configuration​​​​@EnableWs​

@EnableWs
@Configuration
public class EchoConfig {

// @Bean definitions go here

}

要自定义配置,您可以实现或更好地扩展:​​@EnableWs​​​​WsConfigurer​​​​WsConfigurerAdapter​

@Configuration
@EnableWs
@ComponentScan(basePackageClasses = { MyConfiguration.class })
public class MyConfiguration extends WsConfigurerAdapter {

@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(new MyInterceptor());
}

@Override
public void addArgumentResolvers(List<MethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new MyArgumentResolver());
}

// More overridden methods ...
}

在接下来的几节中,将更详细地描述编程模型。​​@Endpoint​

与任何其他 Spring Bean 一样,端点的作用域默认为单例。也就是说,每个容器创建一个 Bean 定义的实例。作为单一实例意味着多个线程可以同时使用它,因此端点必须是线程安全的。如果要使用其他作用域(例如原型),请参阅 Spring 参考文档。

Note that all abstract base classes provided in Spring-WS are thread safe, unless otherwise indicated in the class-level Javadoc.

5.3.1. 处理方法​​@Endpoint​

若要使终结点实际处理传入的 XML 消息,它需要具有一个或多个处理方法。处理方法可以采用广泛的参数和返回类型。但是,它们通常有一个包含消息有效负载的参数,并返回响应消息的有效负载(如果有)。本节介绍支持哪些参数和返回类型。

为了指示方法可以处理的消息类型,该方法通常使用 或 注释进行批注。您可以在终端节点映射中了解有关这些注释的更多信息。​​@PayloadRoot​​​​@SoapAction​

以下示例显示了一种处理方法:

@PayloadRoot(localPart = "order", namespace = "http://samples")
public void order(@RequestPayload Element orderElement) {
Order order = createOrder(orderElement);
orderService.createOrder(order);
}

该方法采用 (用 ) 批注 )作为参数。这意味着消息的有效负载作为 DOM 元素在此方法上传递。该方法具有返回类型,指示不发送响应消息。​​order​​​​Element​​​​@RequestPayload​​​​void​

处理方法参数

处理方法通常具有一个或多个参数,这些参数引用传入 XML 消息的各个部分。最常见的是,处理方法具有映射到消息有效负载的单个参数,但它也可以映射到请求消息的其他部分,例如 SOAP 标头。本节介绍可以在处理方法签名中使用的参数。

要将参数映射到请求消息的有效负载,您需要使用注释来注释此参数。这个注释告诉 Spring-WS 参数需要绑定到请求有效负载。​​@RequestPayload​

下表描述了支持的参数类型。它显示支持的类型、是否应使用 注释参数以及任何其他注释。​​@RequestPayload​

名字

支持的参数类型

​@RequestPayload​​必填?

其他说明

TrAX

​javax.xml.transform.Source​​​和子接口(、、 和​​DOMSource​​​​SAXSource​​​​StreamSource​​​​StAXSource​​)

是的

默认启用。

W3C DOM

​org.w3c.dom.Element​

是的

默认启用

Dom4j

​org.dom4j.Element​

是的

当 dom4j 位于类路径上时启用。

杰登

​org.jdom.Element​

是的

当 JDOM 位于类路径上时启用。

XOM

​nu.xom.Element​

是的

当 XOM 位于类路径上时启用。

斯塔克斯

​javax.xml.stream.XMLStreamReader​​​和​​javax.xml.stream.XMLEventReader​

是的

当 StAX 位于类路径上时启用。

XPath

任何布尔值、双精度型、、 或类型,可以通过 Spring 转换服务从 转换,​并用 .​​String​​​​org.w3c.Node​​​​org.w3c.dom.NodeList​​​​String​​​​@XPathParam​

默认情况下启用,请参阅名为 XPathParam 的部分。

消息上下文

​org.springframework.ws.context.MessageContext​

默认启用。

肥皂

​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`s when used in combination with the `@SoapHeader​

默认启用。

JAXB2

用 和 注释的任何类型。​​javax.xml.bind.annotation.XmlRootElement​​​​javax.xml.bind.JAXBElement​

是的

当 JAXB2 位于类路径上时启用。

奥克姆

Spring OXM Unmarshaller 支持的任何类型。

是的

指定属性 时启用。​​unmarshaller​​​​<sws:annotation-driven/>​

接下来的几个示例显示了可能的方法签名。以下方法以 DOM 的形式调用请求消息的有效负载:​​org.w3c.dom.Element​

public void handle(@RequestPayload Element element)

调用以下方法时,请求消息的有效负载为 .该参数绑定到请求消息的 SOAP 标头。​​javax.xml.transform.dom.DOMSource​​​​header​

public void handle(@RequestPayload DOMSource domSource, SoapHeader header)

调用以下方法时,请求消息的有效负载解组到 (用 注释)。消息的有效载荷也以 DOM 的形式给出。整个消息上下文作为第三个参数传递。​​MyJaxb2Object​​​​@XmlRootElement​​​​Element​

public void handle(@RequestPayload MyJaxb2Object requestObject, @RequestPayload Element element, Message messageContext)

如您所见,在定义如何处理方法签名时,有很多可能性。您甚至可以扩展此机制以支持您自己的参数类型。请参阅 DefaultMethodEndpointAdapter 和 MethodArgumentResolver 的 Javadoc 以了解操作方法。

​@XPathParam​

一种参数类型需要一些额外的解释:。这里的想法是,使用 XPath 表达式批注一个或多个方法参数,并且每个此类批注参数都绑定到表达式的计算。以下示例演示如何执行此操作:​​@XPathParam​

package samples;

@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 表达式中使用前缀,因此必须将其绑定到命名空间。这是通过注释完成的。或者,我们可以将此注释放在类型级别,以便对所有处理程序方法使用相同的命名空间映射,甚至可以将包级别(in )用于多个端点。​​s​​​​http://samples​​​​@Namespace​​​​package-info.java​

通过使用 ,可以绑定到 XPath 支持的所有数据类型:​​@XPathParam​

  • ​boolean​​或Boolean
  • ​double​​或Double
  • ​String​
  • ​Node​
  • ​NodeList​

除了此列表之外,您还可以使用任何可以通过 Spring 转换服务转换的类型。​​String​

处理方法返回类型

若要发送响应消息,处理需要指定返回类型。如果不需要响应消息,该方法可以声明返回类型。最常见的是,返回类型用于创建响应消息的有效负载。但是,您也可以映射到响应消息的其他部分。本节介绍可在处理方法签名中使用的返回类型。​​void​

若要将返回值映射到响应消息的有效负载,需要使用注释对方法进行注释。这个注释告诉 Spring-WS 返回值需要绑定到响应有效负载。​​@ResponsePayload​

下表描述了支持的返回类型。它显示支持的类型、是否应使用 注释参数以及任何其他注释。​​@ResponsePayload​

名字

支持的返回类型

​@ResponsePayload​​必填?

其他说明

无响应

​void​

默认启用。

TrAX

​javax.xml.transform.Source​​​和子接口(、、 和​​DOMSource​​​​SAXSource​​​​StreamSource​​​​StAXSource​​)

是的

默认启用。

W3C DOM

​org.w3c.dom.Element​

是的

默认启用

Dom4j

​org.dom4j.Element​

是的

当 dom4j 位于类路径上时启用。

杰登

​org.jdom.Element​

是的

当 JDOM 位于类路径上时启用。

XOM

​nu.xom.Element​

是的

当 XOM 位于类路径上时启用。

JAXB2

用 和 注释的任何类型。​​javax.xml.bind.annotation.XmlRootElement​​​​javax.xml.bind.JAXBElement​

是的

当 JAXB2 位于类路径上时启用。

奥克姆

弹簧 OXM 编组支持的任何类型。

是的

指定属性 时启用。​​marshaller​​​​<sws:annotation-driven/>​

在定义处理方法签名时,有很多可能性。甚至可以扩展此机制以支持您自己的参数类型。请参阅 DefaultMethodEndpointAdapter 和 MethodReturnValueHandler 的类级 Javadoc 以了解操作方法。

5.4. 端点映射

终结点映射负责将传入消息映射到相应的终结点。默认情况下,某些端点映射处于启用状态,例如,或 .但是,我们首先需要检查 .​​PayloadRootAnnotationMethodEndpointMapping​​​​SoapActionAnnotationMethodEndpointMapping​​​​EndpointMapping​

PROVIDER a 包含与传入请求匹配的终结点,还可能包含应用于请求和响应的终结点拦截器列表。当请求进来时,将其交给端点映射,让它检查请求并提出适当的 .然后调用终结点和链中的任何拦截器。​​EndpointMapping​​​​EndpointInvocationChain​​​​MessageDispatcher​​​​EndpointInvocationChain​​​​MessageDispatcher​

可配置端点映射的概念非常强大,可以选择包含拦截器(反过来,拦截器可以操作请求和/或响应)。许多支持功能可以内置到自定义实现中。例如,自定义终结点映射不仅可以基于消息的内容,还可以根据特定的 SOAP 标头(或实际上多个 SOAP 标头)来选择终结点。​​EndpointMapping​

大多数端点映射继承自 ,它提供了一个“拦截器”属性,该属性是要使用的拦截器列表。 在拦截请求 — 端点拦截器接口中进行了讨论。此外,还有 ,这是在此终结点映射未生成匹配终结点时使用的默认终结点。​​AbstractEndpointMapping​​​​EndpointInterceptors​​​​defaultEndpoint​

如终结点中所述,该样式允许您在一个终结点类中处理多个请求。这是 的责任。此映射确定要为传入请求消息调用的方法。​​@Endpoint​​​​MethodEndpointMapping​

有两个终结点映射可以将请求定向到方法:和 您可以通过在应用程序上下文中使用来启用这两种方法。​​PayloadRootAnnotationMethodEndpointMapping​​​​SoapActionAnnotationMethodEndpointMapping​​​​<sws:annotation-driven/>​

使用 带有 and 元素的批注来标记具有特定限定名称的方法。每当消息传入时,只要消息带有有效负载根元素的此限定名称,就会调用该方法。有关示例,请参见上文。​​PayloadRootAnnotationMethodEndpointMapping​​​​@PayloadRoot​​​​localPart​​​​namespace​

或者,使用 注释来标记具有特定 SOAP 操作的方法。 每当消息带有此标头时,都会调用该方法。​​SoapActionAnnotationMethodEndpointMapping​​​​@SoapAction​​​​SOAPAction​

5.4.1. WS 寻址

WS 寻址指定与传输无关的路由机制。它基于 和 SOAP 标头,它们分别指示 SOAP 消息的目标和意图。此外,WS 寻址允许您定义返回地址(用于正常消息和错误)和唯一消息标识符,该标识符可用于关联。有关 WS 寻址的详细信息,请参阅 https://en.wikipedia.org/wiki/WS-Addressing。下面的示例显示 WS 寻址消息:​​To​​​​Action​

<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 响应)发送响应,但它也可以是另一个地址,如本例所示。​​http://example/com/fabrikam​​​​http://example.com/fabrikam/mail/Delete​

在Spring Web Services中,WS-Addressing是作为端点映射实现的。通过使用此映射,可以将 WS 寻址操作与终结点相关联,类似于前面所述的操作。​​SoapActionAnnotationMethodEndpointMapping​

用​​AnnotationActionEndpointMapping​

类似于 ,但使用 WS 寻址标头而不是 SOAP 操作传输标头。​​AnnotationActionEndpointMapping​​​​SoapActionAnnotationMethodEndpointMapping​

要使用 ,请使用批注批注处理方法,类似于@Endpoint处理方法和终结点映射中所述的 和批注。以下示例演示如何执行此操作:​​AnnotationActionEndpointMapping​​​​@Action​​​​@PayloadRoot​​​​@SoapAction​

package samples;

@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 寻址为 的请求路由到该方法。请求被路由到方法。.​​Action​​​​http://samples/RequestOrder​​​​getOrder​​​​http://samples/CreateOrder​​​​order​

默认情况下,同时支持 1.0(2006 年 5 月)和 2004 年 8 月版的 WS 寻址。这两个版本最受欢迎,可与 Axis 1 和 2、JAX-WS、XFire、Windows Communication Foundation (WCF) 和 Windows Services Enhancements (WSE) 3.0 互操作。如有必要,可以将特定版本的规范注入到属性中。​​AnnotationActionEndpointMapping​​​​versions​

除了批注之外,还可以使用批注对类进行批注。如果设置,则将该值与传入消息的标头属性进行比较。​​@Action​​​​@Address​​​​To​

最后,还有将响应消息发送到非匿名、越界地址所必需的属性。您可以在此属性中设置实现,与在 .请参阅 URI 和传输。​​messageSenders​​​​MessageSender​​​​WebServiceTemplate​

5.4.2. 拦截请求 — 接口​​EndpointInterceptor​

终结点映射机制具有终结点拦截器的概念。当您希望将特定功能应用于某些请求时,例如,处理与安全相关的 SOAP 标头或记录请求和响应消息,这些功能非常有用。

终结点拦截器通常通过使用应用程序上下文中的元素来定义。在此元素中,您可以定义应用于该应用程序上下文中定义的所有端点的端点拦截器 Bean。或者,可以使用 or 元素指定拦截器应应用的有效负载根名称或 SOAP 操作。以下示例演示如何执行此操作:​​<sws:interceptors>​​​​<sws:payloadRoot>​​​​<sws:soapAction>​

<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 class="samples.MySoapActionInterceptor2"/>

在前面的示例中,我们定义了一个拦截所有请求和响应的“全局”拦截器 ()。我们还定义了一个拦截器,该拦截器仅适用于具有 作为有效负载根命名空间的 XML 消息。除了 之外,我们还可以定义一个属性,以进一步限制拦截器适用的消息。最后,我们定义两个拦截器,当消息具有 SOAP 操作时应用这两个拦截器。请注意,第二个拦截器实际上是对元素外部的 Bean 定义的引用。您可以在元素内的任何位置使用 Bean 引用。​​MyGlobalInterceptor​​​​http://www.example.com​​​​localPart​​​​namespaceUri​​​​http://www.example.com/SoapAction​​​​<interceptors>​​​​<interceptors>​

使用类时,可以从 扩展 以添加拦截器:​​@Configuration​​​​WsConfigurerAdapter​

@Configuration
@EnableWs
public class MyWsConfiguration extends WsConfigurerAdapter {

@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(new MyPayloadRootInterceptor());
}

}

拦截器必须实现包中的接口。此接口定义了三种方法,一种可用于在处理实际终结点之前处理请求消息,一种可用于处理正常响应消息,另一种可用于处理错误消息。后两个在处理终结点调用。这三种方法应该提供足够的灵活性来进行各种预处理和后处理。​​EndpointInterceptor​​​​org.springframework.ws.server​

拦截器上的方法返回一个布尔值。您可以使用此方法中断或继续处理调用链。当此方法返回时,端点处理链将继续。当它返回时,解释为拦截器本身已经处理好了事情,并且不会继续处理其他拦截器和调用链中的实际端点。和方法也有一个布尔返回值。当这些方法返回时,响应将不会发送回客户端。​​handleRequest(..)​​​​true​​​​false​​​​MessageDispatcher​​​​handleResponse(..)​​​​handleFault(..)​​​​false​

可以在 Web 服务中使用许多标准实现。此外,还有 ,在 XwsSecurityInterceptor 中进行了描述。​​EndpointInterceptor​​​​XwsSecurityInterceptor​

​PayloadLoggingInterceptor​​和​​SoapEnvelopeLoggingInterceptor​

开发 Web 服务时,记录传入和传出的 XML 消息可能很有用。Spring WS 通过 and 类来促进这一点。前者仅将消息的有效负载记录到共享资源日志记录日志中。后者记录整个 SOAP 信封,包括 SOAP 标头。以下示例演示如何在终结点映射中定义 :​​PayloadLoggingInterceptor​​​​SoapEnvelopeLoggingInterceptor​​​​PayloadLoggingInterceptor​

<sws:interceptors>
<bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>
</sws:interceptors>

这两个侦听器都有两个属性,以及 ,可以将其设置为 以禁用请求或响应消息的日志记录。​​logRequest​​​​logResponse​​​​false​

如前所述,您也可以将该方法用于 。​​WsConfigurerAdapter​​​​PayloadLoggingInterceptor​

​PayloadValidatingInterceptor​

使用协定优先开发风格的好处之一是,我们可以使用架构来验证传入和传出的 XML 消息。Spring-WS 通过 .此侦听器需要引用一个或多个 W3C XML 或 RELAX NG 架构,并且可以设置为验证请求和/或响应。​​PayloadValidatingInterceptor​

请注意,请求验证听起来是个好主意,但它会使生成的 Web 服务非常严格。通常,请求是否验证并不重要,只有当端点可以获得足够的信息来满足请求时。验证响应是一个好主意,因为终结点应遵循其架构。记住波斯特尔定律: “做事要保守;在你接受别人的东西上要*。

以下示例使用 .在此示例中,我们使用架构 in 来验证响应,而不是验证请求。请注意,还可以通过设置属性来接受多个架构。​​PayloadValidatingInterceptor​​​​/WEB-INF/orders.xsd​​​​PayloadValidatingInterceptor​​​​schemas​

<bean 
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>

当然,您也可以将该方法用于 ,如前所述。​​WsConfigurerAdapter​​​​PayloadValidatingInterceptor​

用​​PayloadTransformingInterceptor​

为了将有效负载转换为另一种XML格式,Spring Web Services提供了.此终结点侦听器基于 XSLT 样式表,在支持 Web 服务的多个版本时特别有用,因为您可以将较旧的消息格式转换为较新的格式。以下示例使用 :​​PayloadTransformingInterceptor​​​​PayloadTransformingInterceptor​

<bean 
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​

You could use the approach, as described earlier, for the as well.​​WsConfigurerAdapter​​​​PayloadTransformingInterceptor​

5.5. Handling Exceptions

Spring-WS 可以减轻在消息由与请求匹配的端点处理时发生意外异常的痛苦。端点异常解析程序有点类似于可在 Web 应用程序描述符中定义的异常映射。但是,它们提供了一种更灵活的方法来处理异常。它们提供有关引发异常时调用的终结点的信息。此外,处理异常的编程方式为您提供了更多有关如何适当响应的选项。您不必通过提供异常和堆栈跟踪来公开应用程序的内部,而是可以按照您想要的任何方式处理异常,例如,通过返回具有特定错误代码和字符串的 SOAP 错误。​​EndpointExceptionResolvers​​​​web.xml​

端点异常解析程序由 自动选取,因此无需显式配置。​​MessageDispatcher​

除了实现接口(这只是实现方法的问题)之外,您还可以使用提供的实现之一。最简单的实现是 ,它创建 SOAP 1.1 服务器或 SOAP 1.2 接收器错误,并使用异常消息作为错误字符串。这是默认值,但可以通过显式添加另一个解析程序来覆盖它。​​EndpointExceptionResolver​​​​resolveException(MessageContext, endpoint, Exception)​​​​SimpleSoapExceptionResolver​​​​SimpleSoapExceptionResolver​

5.5.1.​​SoapFaultMappingExceptionResolver​

这是一个更复杂的实现。此解析程序允许您获取可能引发的任何异常的类名,并将其映射到 SOAP 错误:​​SoapFaultMappingExceptionResolver​

<beans>
<bean
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>

键值和默认端点使用 的格式为 ,其中只需要错误代码。如果未设置错误字符串,则默认为异常消息。如果未设置语言,则默认为英语。上述配置将类型的异常映射到错误字符串为 的客户端 SOAP 错误,如下所示:​​faultCode,faultString,locale​​​​ValidationFailureException​​​​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>

如果发生任何其他异常,它将返回默认错误:以异常消息作为错误字符串的服务器端错误。

5.5.2. 使用​​SoapFaultAnnotationExceptionResolver​

还可以使用注释对异常类进行批注,以指示在引发异常时应返回的 SOAP 错误。要选取这些注释,您需要将 添加到应用程序上下文中。注释的元素包括错误代码枚举、错误字符串或原因以及语言。以下示例显示了此类异常:​​@SoapFault​​​​SoapFaultAnnotationExceptionResolver​

package samples;

@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>

5.6. 服务器端测试

在测试 Web 服务终结点时,有两种可能的方法:

  • 编写单元测试,在其中提供(模拟)参数供终结点使用。

    这种方法的优点是很容易完成(特别是对于用 注释的类)。缺点是,您并没有真正测试通过网络发送的 XML 消息的确切内容。@Endpoint
  • 编写集成测试,测试消息的内容。

第一种方法可以通过模拟框架(如EasyMock,JMock等)轻松实现。下一节重点介绍如何使用 Spring Web Services 2.0 中引入的测试功能编写集成测试。

5.6.1. 编写服务器端集成测试

Spring Web Services 2.0引入了对创建端点集成测试的支持。在此上下文中,终结点是处理 (SOAP) 消息的类(请参阅终结点)。

集成测试支持位于包中。该包中的核心类是 .其基本思想是,此客户端创建一个请求消息,然后将其发送到在标准应用程序上下文中配置的端点(请参阅 MessageDispatcherServlet)。这些终结点处理消息并创建响应。然后,客户端收到此响应,并根据注册的期望对其进行验证。​​org.springframework.ws.test.server​​​​MockWebServiceClient​​​​MessageDispatcherServlet​

的典型用法是:。​​MockWebServiceClient​

  1. 通过调用 或 创建实例。MockWebServiceClientMockWebServiceClient.createClient(ApplicationContext)MockWebServiceClient.createClient(WebServiceMessageReceiver, WebServiceMessageFactory)
  2. 通过调用 来发送请求消息,可能使用 中提供的默认实现(可以静态导入)。sendRequest(RequestCreator)RequestCreatorRequestCreators
  3. 通过调用 来设置响应期望,可能使用 中提供的默认实现(可以静态导入)。可以通过链接呼叫来设置多个期望。andExpect(ResponseMatcher)ResponseMatcherResponseMatchersandExpect(ResponseMatcher)

请注意,(和相关类)提供了“流畅”的 API,因此您通常可以使用 IDE 中的代码完成功能来指导您完成设置模拟服务器的过程。​​MockWebServiceClient​

另请注意,在单元测试中,您可以依赖 Spring Web Services 中提供的标准日志记录功能。有时,检查请求或响应消息以找出特定测试失败的原因可能很有用。有关详细信息,请参阅消息日志记录和跟踪。

例如,请考虑以下 Web 服务终结点类:

@Endpoint                                                                
public class CustomerEndpoint {

@ResponsePayload
public CustomerCountResponse getCustomerCount(
@RequestPayload CustomerCountRequest request) {
CustomerCountResponse response = new CustomerCountResponse();
response.setCustomerCount(10);
return response;
}

}

用 注释的 in 。请参阅端点​。​​CustomerEndpoint​​​​@Endpoint​

该方法将 a 作为其参数并返回 .这两个类都是由编组器支持的对象。例如,它们可以具有 JAXB2 支持的注释。​​getCustomerCount()​​​​CustomerCountRequest​​​​CustomerCountResponse​​​​@XmlRootElement​

以下示例显示了 的典型测试:​​CustomerEndpoint​

@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));
}
}


导入和静态导入和 .​​CustomerEndpointIntegrationTest​​​​MockWebServiceClient​​​​RequestCreators​​​​ResponseMatchers​


此测试使用 Spring 框架中提供的标准测试工具。这不是必需的,但通常是设置测试的最简单方法。


应用程序上下文是标准的 Spring-WS 应用程序上下文(参见 ​​MessageDispatcherServlet​​​),从 读取。在这种情况下,应用程序上下文包含 的 Bean 定义(或者可能使用 )。​​spring-ws-servlet.xml​​​​CustomerEndpoint​​​​<context:component-scan />​


在方法中,我们使用工厂方法创建一个。​​@Before​​​​MockWebServiceClient​​​​createClient​


我们通过调用静态导入提供的 a 来发送请求(请参阅​​使用 RequestCreator 和 RequestCreators​​​)。​​sendRequest()​​​​withPayload()​​​​RequestCreator​​​​RequestCreators​


我们还通过使用静态导入提供的调用来设置响应期望(请参阅使用响应匹配​​器和响应匹配器​​​)。​​andExpect()​​​​payload()​​​​ResponseMatcher​​​​ResponseMatchers​



测试的这一部分可能看起来有点混乱,但 IDE 的代码完成功能非常有用。键入 后,IDE 可以为您提供可能的请求创建策略列表,前提是您是静态导入的。这同样适用于 ,前提是您静态导入 。​​sendRequest(​​​​RequestCreators​​​​andExpect()​​​​ResponseMatchers​


5.6.2. 使用 和​​RequestCreator​​​​RequestCreators​

最初,需要创建请求消息供终结点使用。客户端使用策略接口来实现此目的:​​MockWebServiceClient​​​​RequestCreator​

public interface RequestCreator {

WebServiceMessage createRequest(WebServiceMessageFactory messageFactory)
throws IOException;

}

您可以编写自己的此接口实现,使用消息工厂创建请求消息,但当然不必这样做。该类提供了一种基于方法中的给定有效负载创建的方法。您通常静态导入 .​​RequestCreators​​​​RequestCreator​​​​withPayload()​​​​RequestCreators​

5.6.3. 使用 和​​ResponseMatcher​​​​ResponseMatchers​

当端点处理了请求消息并收到响应后,可以验证此响应消息是否满足特定预期。客户端使用策略接口来实现此目的:​​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));

有关 提供的响应匹配器的更多信息,请参阅 Javadoc。​​ResponseMatchers​