如需使用最新稳定版本,请使用 Spring Integration 7.0.4spring-doc.cadn.net.cn

Web Services 支持

本章介绍 Spring Integration 对 Web 服务的支持,包括:spring-doc.cadn.net.cn

您需要将以下依赖项包含到您的项目中:spring-doc.cadn.net.cn

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

出站 Web 服务网关

当您向通道发送消息以调用 Web 服务时,有两种选项,两者都基于 Spring Web Services 项目:SimpleWebServiceOutboundGatewayMarshallingWebServiceOutboundGateway。 前者接受 Stringjavax.xml.transform.Source 作为消息负载。 后者支持任何实现 MarshallerUnmarshaller 接口的类。 两者都需要一个 Spring Web Services DestinationProvider,以确定要调用的 Web 服务的 URI。 以下示例展示了调用 Web 服务的两种选项:spring-doc.cadn.net.cn

 simpleGateway = new SimpleWebServiceOutboundGateway(destinationProvider);

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

从版本 5.0 开始,您可以使用外部 WebServiceTemplate 实例提供 SimpleWebServiceOutboundGatewayMarshallingWebServiceOutboundGateway,该实例可配置任何自定义属性,包括 checkConnectionForFault(允许您的应用程序处理不符合规范的服务)。spring-doc.cadn.net.cn

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

入站 Web 服务网关

当接收到 Web 服务调用并向通道发送消息时,您仍然有两个选项:SimpleWebServiceInboundGatewayMarshallingWebServiceInboundGateway。 前者从 WebServiceMessage 中提取 javax.xml.transform.Source 并将其设置为消息负载。 后者支持实现 MarshallerUnmarshaller 接口。 如果传入的 Web 服务消息是 SOAP 消息,则 SOAP 操作头将添加到转发到请求通道的 Message 的头中。 以下示例展示了这两种选项:spring-doc.cadn.net.cn

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

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

两个网关都实现了 Spring Web Services MessageEndpoint 接口,因此可以按照标准 Spring Web Services 配置方式,使用 MessageDispatcherServlet 进行配置。spring-doc.cadn.net.cn

关于如何使用这些组件的更多详细信息,请参阅 Spring Web Services 参考指南中涵盖 创建 Web 服务 的章节。 涵盖 对象/XML 映射 的章节同样适用。spring-doc.cadn.net.cn

要将 SimpleWebServiceInboundGatewayMarshallingWebServiceInboundGateway 配置添加到 Spring WS 基础设施中,您应该像处理普通 Spring WS 应用程序一样,在 MessageDispatcherServlet 和目标 MessageEndpoint 实现之间添加 EndpointMapping 定义。 为此(从 Spring Integration 的角度),Spring WS 提供了以下便捷的 EndpointMapping 实现:spring-doc.cadn.net.cn

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

查看 端点映射 以获取更多信息。spring-doc.cadn.net.cn

Web 服务命名空间支持

要配置出站 Web 服务网关,请使用来自 ws 命名空间的 outbound-gateway 元素,如下例所示:spring-doc.cadn.net.cn

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

要设置入站 Web 服务网关,请使用 inbound-gateway 元素,如下例所示:spring-doc.cadn.net.cn

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

要使用 Spring OXM 的编组器或解组器,您必须提供 Bean 引用。 以下示例展示了如何为出站编组网关提供 Bean 引用:spring-doc.cadn.net.cn

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

以下示例展示了如何为入站编组网关提供 Bean 引用:spring-doc.cadn.net.cn

<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 Services DestinationProvider 实现(例如,用于在运行时从注册表中查找 URI)。spring-doc.cadn.net.cn

对于任意出站网关类型,message-factory属性也可以配置为对任何Spring Web Services WebServiceMessageFactory实现的引用。spring-doc.cadn.net.cn

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

从 5.0 版本开始,web-service-template 引用属性允许您注入任何可能的自定义属性的 WebServiceTemplatespring-doc.cadn.net.cn

Web Service Java DSL 支持

以下代码片段展示了在 Web Service 命名空间支持 中显示的网关的等效配置:spring-doc.cadn.net.cn

@Bean
IntegrationFlow inbound() {
    return IntegrationFlow.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 IntegrationFlow.from(Ws.marshallingInboundGateway()
                .marshaller(someMarshaller())
                .unmarshaller(someUnmarshalller())
                .id("marshallingGateway"))
        ...
        .get();
}

其他属性可以以流畅的方式设置在端点规范上(具体取决于是否为出站网关提供了外部WebServiceTemplate)。 示例:spring-doc.cadn.net.cn

.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 方案(请参阅 URI 和传输),提供 <uri-variable/> 替换功能。 以下示例展示了如何定义它:spring-doc.cadn.net.cn

<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,则不支持变量替换,并且在您提供变量时会发生配置错误。spring-doc.cadn.net.cn

控制 URI 编码

默认情况下,URL 字符串在发送请求之前会被编码为 URI 对象(见 UriComponentsBuilder)。 在某些涉及非标准 URI 的场景中,可能不希望进行编码操作。 <ws:outbound-gateway/> 元素提供了一个 encoding-mode 属性。 要禁用 URL 编码,请将此属性设置为 NONE(默认值为 TEMPLATE_AND_VALUES)。 如果您希望仅对 URL 的部分内容进行编码,可以通过在 <uri-variable/> 中使用 expression 来实现,如下例所示:spring-doc.cadn.net.cn

<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 消息头

The Spring Integration web service gateways automatically map the SOAP action header. By default, it is copied to and from Spring Integration MessageHeaders by using the DefaultSoapHeaderMapper.spring-doc.cadn.net.cn

您可以传入您自己实现的 SOAP 特定头映射器,因为网关具有支持此操作的属性。spring-doc.cadn.net.cn

除非 requestHeaderNamesreplyHeaderNames 属性明确指定,否则任何用户定义的 SOAP 头都不会被复制到 SOAP 消息中,也不会从 SOAP 消息中复制出来。spring-doc.cadn.net.cn

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

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

从 4.1 版本开始,AbstractHeaderMapper(一个 DefaultSoapHeaderMapper 的超类)允许为 NON_STANDARD_HEADERS Tokens配置 requestHeaderNamesreplyHeaderNames 属性(除了现有的 STANDARD_REQUEST_HEADERSSTANDARD_REPLY_HEADERS),以映射所有用户定义的头部。spring-doc.cadn.net.cn

与其使用通配符 (*),我们建议使用以下组合:STANDARD_REPLY_HEADERS, NON_STANDARD_HEADERS。 这样做可避免将 request 请求头映射到响应中。

从版本 4.3 开始,您可以通过在模式前添加 ! 来否定标头映射中的模式。 被否定的模式具有优先级,因此像 STANDARD_REQUEST_HEADERS,thing1,thing*,!thing2,!thing3,qux,!thing1 这样的列表不会映射 thing1thing2thing3。 它会映射标准标头 thing4qux。 (注意:thing1 同时包含在非否定和否定形式中。 由于否定值具有优先级,thing1 不会被映射。)spring-doc.cadn.net.cn

如果您有一个以 ! 开头的用户自定义头部,且您希望对其进行映射,您可以使用 \ 进行转义,如下所示:STANDARD_REQUEST_HEADERS,\!myBangHeader。 此时,!myBangHeader 将被映射。

入站 SOAP 消息头(入站网关的请求消息头和出站网关的回复消息头)被映射为 SoapHeaderElement 对象。 您可以通过访问 Source 来查看其内容:spring-doc.cadn.net.cn

<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 头,但不映射 quxspring-doc.cadn.net.cn

下面的示例展示了如何从名为 auth 的头部中获取名为 user 的值:spring-doc.cadn.net.cn

...
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> 的子节点进行填充。 以下示例展示了如何实现:spring-doc.cadn.net.cn

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 信封:spring-doc.cadn.net.cn

<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 服务网关通过 marshaller 的内置功能直接支持附件(例如,Jaxb2Marshaller 提供了 mtomEnabled 选项)。 从版本 5.0 开始,简单的 Web 服务网关可以直接处理入站和出站的 MimeMessage 实例,这些实例拥有用于操作附件的 API。 当您需要发送带有附件的 Web 服务消息(无论是来自服务器的回复还是客户端请求)时,您应该直接使用 WebServiceMessageFactory,并将带有附件的 WebServiceMessage 作为 payload 发送到网关的请求或回复通道。 以下示例展示了如何操作:spring-doc.cadn.net.cn

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