网络服务支持

本章介绍 Spring Integration 对 Web 服务的支持,包括:

您需要将此依赖项包含到您的项目中:

Maven
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-ws</artifactId>
    <version>5.5.13</version>
</dependency>
Gradle
compile "org.springframework.integration:spring-integration-ws:5.5.13"

出站 Web 服务网关

要在向通道发送消息时调用 Web 服务,您有两个选项,它们都基于Spring Web 服务项目:SimpleWebServiceOutboundGatewayMarshallingWebServiceOutboundGateway. 前者接受 aStringjavax.xml.transform.Source作为消息有效负载。后者支持MarshallerUnmarshaller接口的任何实现。两者都需要 Spring Web ServicesDestinationProvider来确定要调用的 Web 服务的 URI。以下示例显示了调用 Web 服务的两个选项:

 simpleGateway = new SimpleWebServiceOutboundGateway(destinationProvider);

 marshallingGateway = new MarshallingWebServiceOutboundGateway(destinationProvider, marshaller);
当使用命名空间支持(稍后描述)时,您只需要设置一个 URI。在内部,解析器配置一个固定的 URIDestinationProvider实现。但是,如果您需要在运行时动态解析 URI,则DestinationProvider可以提供诸如从注册表中查找 URI 之类的行为。有关此策略的更多信息,请参阅 Spring Web Services DestinationProviderJavadoc。

从 5.0 版开始,您可以为SimpleWebServiceOutboundGatewayandMarshallingWebServiceOutboundGateway提供一个外部WebServiceTemplate实例,您可以为任何自定义属性配置该实例,包括checkConnectionForFault(允许您的应用程序处理不符合要求的服务)。

有关内部工作的更多详细信息,请参阅 Spring Web Services 参考指南中涵盖客户端访问的章节和涵盖对象/XML 映射的章节。

入站 Web 服务网关

要在接收到 Web 服务调用时向通道发送消息,您还有两个选择:SimpleWebServiceInboundGatewayMarshallingWebServiceInboundGateway. 前者javax.xml.transform.Source从 中提取 aWebServiceMessage并将其设置为消息有效负载。后者支持MarshallerUnmarshaller接口的实现。如果传入的 Web 服务消息是 SOAP 消息,则会将 SOAP 操作标头添加到Message转发到请求通道的消息的标头中。以下示例显示了这两个选项:

 simpleGateway = new SimpleWebServiceInboundGateway();
 simpleGateway.setRequestChannel(forwardOntoThisChannel);
 simpleGateway.setReplyChannel(listenForResponseHere); //Optional

 marshallingGateway = new MarshallingWebServiceInboundGateway(marshaller);
 //set request and optionally reply channel

两个网关都实现了 Spring Web ServicesMessageEndpoint接口,因此可以MessageDispatcherServlet按照标准 Spring Web Services 配置对其进行配置。

要将SimpleWebServiceInboundGatewayMarshallingWebServiceInboundGateway配置添加到 Spring WS 基础架构,您应该在和 目标实现EndpointMapping之间添加定义,就像您为普通 Spring WS 应用程序所做的那样。为此(从 Spring 集成的角度来看),Spring WS 提供了以下方便的实现:MessageDispatcherServletMessageEndpointEndpointMapping

  • o.s.ws.server.endpoint.mapping.UriEndpointMapping

  • o.s.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping

  • o.s.ws.soap.server.endpoint.mapping.SoapActionEndpointMapping

  • o.s.ws.server.endpoint.mapping.XPathPayloadEndpointMapping

您必须在应用程序上下文中为这些类指定 bean,并根据 WS 映射算法引用SimpleWebServiceInboundGateway和/或bean 定义。MarshallingWebServiceInboundGateway

有关更多信息,请参阅端点映射

Web 服务命名空间支持

要配置出站 Web 服务网关,请使用命名空间中的outbound-gateway元素ws,如以下示例所示:

<int-ws:outbound-gateway id="simpleGateway"
                     request-channel="inputChannel"
                     uri="https://example.org"/>
此示例不提供“回复通道”。如果 Web 服务返回非空响应,则Message包含该响应的内容将发送到请求消息REPLY_CHANNEL标头中定义的回复通道。如果不可用,则会引发通道解析异常。如果您想将回复发送到另一个通道,请在“出站网关”元素上提供“回复通道”属性。
默认情况下,当您调用的 Web 服务在对请求使用字符串有效负载后返回空响应时Message,不会发送任何回复Message。因此,您无需设置“回复通道”或REPLY_CHANNEL在请求中包含标头Message。如果您确实想以 接收空响应Message,则可以将 'ignore-empty-responses' 属性设置为false。这样做仅适用于String对象,因为使用 aSourceDocument对象会导致空响应,因此永远不会生成回复Message

要设置入站 Web 服务网关,请使用该inbound-gateway元素,如以下示例所示:

<int-ws:inbound-gateway id="simpleGateway"
                    request-channel="inputChannel"/>

要使用 Spring OXM 编组器或解组器,您必须提供 bean 引用。以下示例显示如何为出站编组网关提供 bean 引用:

<int-ws:outbound-gateway id="marshallingGateway"
                     request-channel="requestChannel"
                     uri="https://example.org"
                     marshaller="someMarshaller"
                     unmarshaller="someUnmarshaller"/>

以下示例显示如何为入站编组网关提供 bean 引用:

<int-ws:inbound-gateway id="marshallingGateway"
                    request-channel="requestChannel"
                    marshaller="someMarshaller"
                    unmarshaller="someUnmarshaller"/>
大多数Marshaller实现也实现了Unmarshaller接口。使用这样的Marshaller时,只需要marshaller属性。即使使用Marshaller,您也可以为request-callback出站网关提供参考。

对于任何一种出站网关类型,您都可以指定一个destination-provider属性而不是uri(只需要其中一个)。然后,您可以引用任何 Spring Web 服务DestinationProvider实现(例如,在运行时从注册表中查找 URI)。

对于任一出站网关类型,message-factory还可以使用对任何 Spring Web 服务WebServiceMessageFactory实现的引用来配置属性。

对于简单入站网关类型,您可以将extract-payload属性设置false为将整个WebServiceMessage而不是仅将其有效负载作为 a转发Message到请求通道。这样做可能很有用,例如,当自定义转换器WebServiceMessage直接与 .

从版本 5.0 开始,web-service-template引用属性允许您注入WebServiceTemplate任何可能的自定义属性。

Web 服务 Java DSL 支持

Web 服务命名空间支持中显示的网关的等效配置显示在以下片段中:

@Bean
IntegrationFlow inbound() {
    return IntegrationFlows.from(Ws.simpleInboundGateway()
                .id("simpleGateway"))
        ...
        .get();
}
@Bean
IntegrationFlow outboundMarshalled() {
    return f -> f.handle(Ws.marshallingOutboundGateway()
                    .id("marshallingGateway")
                    .marshaller(someMarshaller())
                    .unmarshaller(someUnmarshalller()))
        ...
}
@Bean
IntegrationFlow inboundMarshalled() {
    return IntegrationFlows.from(Ws.marshallingInboundGateway()
                .marshaller(someMarshaller())
                .unmarshaller(someUnmarshalller())
                .id("marshallingGateway"))
        ...
        .get();
}

可以以流畅的方式在端点规范上设置其他属性(属性取决于是否WebServiceTemplate为出站网关提供了外部)。例子:

.from(Ws.simpleInboundGateway()
                .extractPayload(false))
.handle(Ws.simpleOutboundGateway(template)
            .uri(uri)
            .sourceExtractor(sourceExtractor)
            .encodingMode(DefaultUriBuilderFactory.EncodingMode.NONE)
            .headerMapper(headerMapper)
            .ignoreEmptyResponses(true)
            .requestCallback(requestCallback)
            .uriVariableExpressions(uriVariableExpressions)
            .extractPayload(false))
)
.handle(Ws.marshallingOutboundGateway()
            .destinationProvider(destinationProvider)
            .marshaller(marshaller)
            .unmarshaller(unmarshaller)
            .messageFactory(messageFactory)
            .encodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY)
            .faultMessageResolver(faultMessageResolver)
            .headerMapper(headerMapper)
            .ignoreEmptyResponses(true)
            .interceptors(interceptor)
            .messageSenders(messageSender)
            .requestCallback(requestCallback)
            .uriVariableExpressions(uriVariableExpressions))
.handle(Ws.marshallingOutboundGateway(template)
            .uri(uri)
            .encodingMode(DefaultUriBuilderFactory.EncodingMode.URI_COMPONENT)
            .headerMapper(headerMapper)
            .ignoreEmptyResponses(true)
            .requestCallback(requestCallback)
            .uriVariableExpressions(uriVariableExpressions))
)

出站 URI 配置

对于 Spring Web Services 支持的所有 URI 方案(请参阅URIs 和 Transports<uri-variable/>,提供了替换。以下示例显示了如何定义它:

<ws:outbound-gateway id="gateway" request-channel="input"
        uri="https://springsource.org/{thing1}-{thing2}">
    <ws:uri-variable name="thing1" expression="payload.substring(1,7)"/>
    <ws:uri-variable name="thing2" expression="headers.x"/>
</ws:outbound-gateway>

<ws:outbound-gateway request-channel="inputJms"
        uri="jms:{destination}?deliveryMode={deliveryMode}&amp;priority={priority}"
        message-sender="jmsMessageSender">
    <ws:uri-variable name="destination" expression="headers.jmsQueue"/>
    <ws:uri-variable name="deliveryMode" expression="headers.deliveryMode"/>
    <ws:uri-variable name="priority" expression="headers.jms_priority"/>
</ws:outbound-gateway>

如果您提供DestinationProvider,则不支持变量替换,并且如果您提供变量,则会发生配置错误。

控制 URI 编码

默认情况下,UriComponentsBuilder在发送请求之前,URL 字符串会被编码(参见 )为 URI 对象。在某些使用非标准 URI 的情况下,不希望执行编码。该<ws:outbound-gateway/>元素提供了一个encoding-mode属性。要禁用 URL 编码,请将此属性设置为NONE(默认为TEMPLATE_AND_VALUES)。如果您希望对某些 URL 进行部分编码,可以通过使用expressioninside a来实现<uri-variable/>,如以下示例所示:

<ws:outbound-gateway url="https://somehost/%2f/fooApps?bar={param}" encoding-mode="NONE">
          <http:uri-variable name="param"
            expression="T(org.apache.commons.httpclient.util.URIUtil)
                                             .encodeWithinQuery('Hello World!')"/>
</ws:outbound-gateway>
如果设置DestinationProvider,encoding-mode将被忽略。

WS 消息头

Spring Integration Web 服务网关自动映射 SOAP 操作标头。默认情况下,它MessageHeaders使用DefaultSoapHeaderMapper.

您可以传入您自己的特定于 SOAP 的标头映射器的实现,因为网关具有支持这样做的属性。

除非由 的requestHeaderNamesreplyHeaderNames属性明确指定,否则DefaultSoapHeaderMapper任何用户定义的 SOAP 标头都不会复制到 SOAP 消息或从 SOAP 消息复制。

当您使用 XML 命名空间进行配置时,您可以使用mapped-request-headersmapped-reply-headers属性来设置这些属性,您可以通过设置属性来提供自定义映射器header-mapper

映射用户定义的标头时,值还可以包含简单的通配符模式(例如myheader*myheader)。例如,如果您需要复制所有用户定义的标头,则可以使用通配符: .

从版本 4.1 开始,AbstractHeaderMapperDefaultSoapHeaderMapper超类)允许为and属性NON_STANDARD_HEADERS配置令牌(除了现有的and )以映射所有用户定义的标头。requestHeaderNamesreplyHeaderNamesSTANDARD_REQUEST_HEADERSSTANDARD_REPLY_HEADERS

我们建议使用以下组合,而不是使用通配符 ( *):STANDARD_REPLY_HEADERS, NON_STANDARD_HEADERS. 这样做可以避免将request标头映射到回复。

从版本 4.3 开始,您可以通过在模式前面加上 . 来否定标头映射中的模式!。否定模式获得优先权,因此诸如STANDARD_REQUEST_HEADERS,thing1,thing*,!thing2,!thing3,qux,!thing1不映射thing1thing2或的列表thing3。它确实映射了标准标题thing4、 和qux. (请注意,thing1非否定形式和否定形式都包含它。因为否定值优先,thing1所以不映射。)

!如果您有一个以您希望映射 的开头的用户定义标头,您可以使用 转义它\,如下所示STANDARD_REQUEST_HEADERS,\!myBangHeader!myBangHeader然后映射 A。

入站 SOAP 标头(入站网关的请求标头和出站网关的回复标头)被映射为SoapHeaderElement对象。您可以通过访问来探索内容Source

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Header>
        <auth>
            <username>user</username>
            <password>pass</password>
        </auth>
        <bar>BAR</bar>
        <baz>BAZ</baz>
        <qux>qux</qux>
    </soapenv:Header>
    <soapenv:Body>
        ...
    </soapenv:Body>
</soapenv:Envelope>

如果mapped-request-headersauth, ca*,则authcatcan标头已映射,但未qux映射。

以下示例显示如何user从名为 的标头中获取命名值auth

...
SoapHeaderElement header = (SoapHeaderElement) headers.get("auth");
DOMSource source = (DOMSource) header.getSource();
NodeList nodeList = source.getNode().getChildNodes();
assertEquals("username", nodeList.item(0).getNodeName());
assertEquals("user", nodeList.item(0).getFirstChild().getNodeValue());
...

从 5.0 版开始,DefaultSoapHeaderMapper支持用户定义类型的标头javax.xml.transform.Source并将它们填充为<soapenv:Header>. 以下示例显示了如何执行此操作:

Map<String, Object> headers = new HashMap<>();

String authXml =
     "<auth xmlns='http://test.auth.org'>"
           + "<username>user</username>"
           + "<password>pass</password>"
           + "</auth>";
headers.put("auth", new StringSource(authXml));
...
DefaultSoapHeaderMapper mapper = new DefaultSoapHeaderMapper();
mapper.setRequestHeaderNames("auth");

前面示例的结果是以下 SOAP 信封:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Header>
        <auth xmlns="http://test.auth.org">
            <username>user</username>
            <password>pass</password>
        </auth>
    </soapenv:Header>
    <soapenv:Body>
        ...
    </soapenv:Body>
</soapenv:Envelope>

MTOM 支持

编组入站和出站 Web 服务网关直接通过编组器的内置功能支持附件(例如,Jaxb2Marshaller提供mtomEnabled选项)。从 5.0 版开始,简单的 Web 服务网关可以直接操作入站和出站MimeMessage实例,这些实例具有操作附件的 API。当您需要发送带有附件的 Web 服务消息(来自服务器的回复或客户端请求)时,您应该WebServiceMessageFactory直接使用并将WebServiceMessage带有附件的消息发送payload到网关的请求或回复通道。以下示例显示了如何执行此操作:

WebServiceMessageFactory messageFactory = new SaajSoapMessageFactory(MessageFactory.newInstance());
MimeMessage webServiceMessage = (MimeMessage) messageFactory.createWebServiceMessage();

String request = "<test>foo</test>";

TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.transform(new StringSource(request), webServiceMessage.getPayloadResult());

webServiceMessage.addAttachment("myAttachment", new ByteArrayResource("my_data".getBytes()), "plain/text");

this.webServiceChannel.send(new GenericMessage<>(webServiceMessage));

1. see XML Configuration