有时,您可能需要使用比目标系统提供的信息更多的信息来增强请求。 数据扩充器模式描述了各种方案以及允许您满足此类要求的组件 (Enricher)。Spring中文文档

Spring Integration 模块包括两个扩充器:CoreSpring中文文档

它还包括三个特定于适配器的标头扩充器:Spring中文文档

请参阅本参考手册中特定于适配器的部分,以了解有关这些适配器的更多信息。Spring中文文档

有关表达式支持的更多信息,请参见 Spring 表达式语言 (SpEL)。Spring中文文档

标头 Enricher

如果只需要向消息添加标头,并且标头不是由消息内容动态确定的,那么引用 transformer 的自定义实现可能有点过头了。 因此,Spring Integration 提供了对标头扩充模式的支持。 它通过元素公开。 以下示例演示如何使用它:<header-enricher>Spring中文文档

<int:header-enricher input-channel="in" output-channel="out">
    <int:header name="foo" value="123"/>
    <int:header name="bar" ref="someBean"/>
</int:header-enricher>

标头扩充器还提供了有用的子元素来设置众所周知的标头名称,如以下示例所示:Spring中文文档

<int:header-enricher input-channel="in" output-channel="out">
    <int:error-channel ref="applicationErrorChannel"/>
    <int:reply-channel ref="quoteReplyChannel"/>
    <int:correlation-id value="123"/>
    <int:priority value="HIGHEST"/>
    <routing-slip value="channel1; routingSlipRoutingStrategy; request.headers[myRoutingSlipChannel]"/>
    <int:header name="bar" ref="someBean"/>
</int:header-enricher>

前面的配置表明,对于已知的标头(如 、 、 、 、 等),您可以使用方便的子元素直接设置这些值,而不是使用必须同时提供标头“name”和“value”的通用子元素。errorChannelcorrelationIdpriorityreplyChannelrouting-slip<header>Spring中文文档

从版本 4.1 开始,标头 enricher 提供了一个子元素。 有关详细信息,请参阅 Routing Sliprouting-slipSpring中文文档

POJO 支持

通常,标头值不能静态定义,必须根据消息中的某些内容动态确定。 这就是为什么标头 enricher 还允许您使用 and 属性来指定 Bean 引用的原因。 指定的方法计算标头值。 请考虑以下配置和一个 Bean 的 bean,该 Bean 具有修改 :refmethodStringSpring中文文档

<int:header-enricher input-channel="in" output-channel="out">
    <int:header name="something" method="computeValue" ref="myBean"/>
</int:header-enricher>

<bean id="myBean" class="thing1.thing2.MyBean"/>
public class MyBean {

    public String computeValue(String payload){
        return payload.toUpperCase() + "_US";
    }
}

您还可以将 POJO 配置为内 Bean,如以下示例所示:Spring中文文档

<int:header-enricher  input-channel="inputChannel" output-channel="outputChannel">
    <int:header name="some_header">
        <bean class="org.MyEnricher"/>
    </int:header>
</int:header-enricher>

您可以类似地指向 Groovy 脚本,如以下示例所示:Spring中文文档

<int:header-enricher  input-channel="inputChannel" output-channel="outputChannel">
    <int:header name="some_header">
        <int-groovy:script location="org/SampleGroovyHeaderEnricher.groovy"/>
    </int:header>
</int:header-enricher>

SpEL 支持

在 Spring Integration 2.0 中,我们引入了 Spring 表达式语言 (SpEL) 的便利性,以帮助配置许多不同的组件。 标题 enricher 就是其中之一。 再看前面显示的 POJO 示例。 您可以看到,确定标头值的计算逻辑非常简单。 一个自然的问题是:“有没有更简单的方法来做到这一点? 这就是 SpEL 展示其真正力量的地方。 请看以下示例:Spring中文文档

<int:header-enricher input-channel="in" output-channel="out">
    <int:header name="foo" expression="payload.toUpperCase() + '_US'"/>
</int:header-enricher>

通过将 SpEL 用于此类简单情况,您不再需要提供单独的类并在应用程序上下文中对其进行配置。 您需要做的就是使用有效的 SpEL 表达式配置属性。 “payload”和“headers”变量绑定到 SpEL 评估上下文,使您能够完全访问传入消息。expressionSpring中文文档

使用 Java 配置配置标头 Enricher

以下两个示例演示如何将 Java 配置用于标头扩充器:Spring中文文档

@Bean
@Transformer(inputChannel = "enrichHeadersChannel", outputChannel = "emailChannel")
public HeaderEnricher enrichHeaders() {
    Map<String, ? extends HeaderValueMessageProcessor<?>> headersToAdd =
            Collections.singletonMap("emailUrl",
                      new StaticHeaderValueMessageProcessor<>(this.imapUrl));
    HeaderEnricher enricher = new HeaderEnricher(headersToAdd);
    return enricher;
}

@Bean
@Transformer(inputChannel="enrichHeadersChannel", outputChannel="emailChannel")
public HeaderEnricher enrichHeaders() {
    Map<String, HeaderValueMessageProcessor<?>> headersToAdd = new HashMap<>();
    headersToAdd.put("emailUrl", new StaticHeaderValueMessageProcessor<String>(this.imapUrl));
    Expression expression = new SpelExpressionParser().parseExpression("payload.from[0].toString()");
    headersToAdd.put("from",
               new ExpressionEvaluatingHeaderValueMessageProcessor<>(expression, String.class));
    HeaderEnricher enricher = new HeaderEnricher(headersToAdd);
    return enricher;
}

第一个示例添加单个文本标头。 第二个示例添加两个标头,一个文本标头和一个基于 SpEL 表达式。Spring中文文档

使用 Java DSL 配置标头 Enricher

以下示例显示了标头扩充器的 Java DSL 配置:Spring中文文档

@Bean
public IntegrationFlow enrichHeadersInFlow() {
    return f -> f
                ...
                .enrichHeaders(h -> h.header("emailUrl", this.emailUrl)
                                     .headerExpression("from", "payload.from[0].toString()"))
                .handle(...);
}

标头通道注册表

从 Spring Integration 3.0 开始,可以使用一个新的子元素。 它没有属性。 这个新的子元素将现有和标头(当它们是 )转换为 a 并将通道存储在注册表中,以便以后在需要发送回复或处理错误时进行解析。 这对于标头可能丢失的情况非常有用,例如,在将消息序列化到消息存储库中时,或者在通过 JMS 传输消息时。 如果标头尚不存在,或者它不是 ,则不进行任何更改。<int:header-channels-to-string/>replyChannelerrorChannelMessageChannelStringMessageChannelSpring中文文档

使用此功能需要存在 Bean。 默认情况下,框架会创建一个默认到期时间(60 秒)的数据库。 在此时间之后,频道将从注册表中删除。 要更改此行为,请使用 of 定义一个 bean,并使用构造函数参数(以毫秒为单位)配置所需的默认延迟。HeaderChannelRegistryDefaultHeaderChannelRegistryidintegrationHeaderChannelRegistrySpring中文文档

从版本 4.1 开始,您可以在定义上设置调用 to 的属性,并且首次使用时会立即删除映射条目。 这在高容量环境中以及通道仅使用一次时可能很有用,而不是等待收割者将其删除。removeOnGettrue<bean/>Spring中文文档

有一种方法可以确定注册表的当前大小。 该方法取消当前计划任务并立即运行收割器。 然后,根据当前延迟计划任务再次运行。 可以通过获取对注册表的引用来直接调用这些方法,也可以将包含以下内容的消息发送到控制总线:HeaderChannelRegistrysize()runReaper()Spring中文文档

"@integrationHeaderChannelRegistry.runReaper()"

此子元素为方便起见,等效于指定以下配置:Spring中文文档

<int:reply-channel
    expression="@integrationHeaderChannelRegistry.channelToChannelName(headers.replyChannel)"
    overwrite="true" />
<int:error-channel
    expression="@integrationHeaderChannelRegistry.channelToChannelName(headers.errorChannel)"
    overwrite="true" />

从版本 4.1 开始,您现在可以覆盖注册表配置的收割者延迟,以便通道映射至少保留指定时间,而不管收割者延迟如何。 以下示例演示如何执行此操作:Spring中文文档

<int:header-enricher input-channel="inputTtl" output-channel="next">
    <int:header-channels-to-string time-to-live-expression="120000" />
</int:header-enricher>

<int:header-enricher input-channel="inputCustomTtl" output-channel="next">
    <int:header-channels-to-string
        time-to-live-expression="headers['channelTTL'] ?: 120000" />
</int:header-enricher>

在第一种情况下,每个标头通道映射的生存时间为两分钟。 在第二种情况下,生存时间在消息标头中指定,如果没有标头,则使用 Elvis 运算符使用两分钟。Spring中文文档

有效载荷丰富

在某些情况下,如前所述,标头扩充器可能不够用,有效负载本身可能必须用其他信息扩充。 例如,进入 Spring Integration 消息传递系统的订单消息必须根据提供的客户编号查找订单的客户,然后使用该信息扩充原始有效负载。Spring中文文档

Spring Integration 2.1 引入了有效负载扩充器。 有效负载扩充器定义一个终结点,该终结点将 a 传递到公开的请求通道,然后期望收到回复消息。 然后,回复消息将成为计算表达式的根对象,以丰富目标有效负载。MessageSpring中文文档

有效负载扩充器通过元素提供完整的 XML 命名空间支持。 为了发送请求消息,有效负载扩充器具有一个属性,允许您将消息调度到请求通道。enricherrequest-channelSpring中文文档

基本上,通过定义请求通道,有效负载 enricher 充当网关,等待发送到请求通道的消息返回。 然后,扩充器使用回复消息提供的数据来扩充消息的有效负载。Spring中文文档

将消息发送到请求通道时,您还可以选择使用该属性仅发送原始有效负载的子集。request-payload-expressionSpring中文文档

有效载荷的扩充是通过 SpEL 表达式配置的,从而提供了最大程度的灵活性。 因此,您不仅可以使用来自应答通道的直接值来丰富有效负载,还可以使用 SpEL 表达式从该消息中提取子集或应用其他内联转换,从而进一步操作数据。MessageSpring中文文档

如果只需要使用静态值丰富有效负载,则无需提供该属性。request-channelSpring中文文档

富集器是变压器的一种变体。 在许多情况下,您可以使用有效负载扩充器或通用转换器实现将其他数据添加到消息有效负载中。 您应该熟悉 Spring Integration 提供的所有支持转换的组件,并仔细选择语义上最适合您的业务案例的实现。

配置

以下示例显示了有效负载扩充器的所有可用配置选项:Spring中文文档

<int:enricher request-channel=""                           (1)
              auto-startup="true"                          (2)
              id=""                                        (3)
              order=""                                     (4)
              output-channel=""                            (5)
              request-payload-expression=""                (6)
              reply-channel=""                             (7)
              error-channel=""                             (8)
              send-timeout=""                              (9)
              should-clone-payload="false">                (10)
    <int:poller></int:poller>                              (11)
    <int:property name="" expression="" null-result-expression="'Could not determine the name'"/>   (12)
    <int:property name="" value="23" type="java.lang.Integer" null-result-expression="'0'"/>
    <int:header name="" expression="" null-result-expression=""/>   (13)
    <int:header name="" value="" overwrite="" type="" null-result-expression=""/>
</int:enricher>
1 将消息发送到的通道以获取用于扩充的数据。 自选。
2 生命周期属性指示是否应在应用程序上下文启动期间启动此组件。 默认值为 true。 自选。
3 基础 Bean 定义的 ID,即 an 或 . 自选。EventDrivenConsumerPollingConsumer
4 指定此终结点作为订阅者连接到通道时的调用顺序。 当该通道使用“故障转移”调度策略时,这一点尤其重要。 当此终结点本身是具有队列的通道的轮询使用者时,它不起作用。 自选。
5 标识在此终结点处理消息后发送消息的消息通道。 自选。
6 默认情况下,原始消息的有效负载用作发送到 . 通过将 SpEL 表达式指定为属性的值,可以使用原始负载的子集、标头值或任何其他可解析的 SpEL 表达式作为发送到请求通道的负载的基础。 对于表达式计算,完整消息可作为“根对象”使用。 例如,以下 SpEL 表达式(以及其他表达式)是可能的:、、、request-channelrequest-payload-expressionpayload.somethingheaders.somethingnew java.util.Date()'thing1' + 'thing2'
7 预期会收到回复消息的频道。 这是可选的。 通常,自动生成的临时回复通道就足够了。 自选。
8 如果 an 发生在 的下游,则将 an 发送到的通道。 这使您能够返回用于扩充的替代对象。 如果未设置,则将向调用方抛出 an。 自选。ErrorMessageExceptionrequest-channelException
9 向通道发送消息时等待的最长时间(以毫秒为单位),如果通道可能阻塞。 例如,如果已达到队列通道的最大容量,则队列通道可以阻塞,直到空间可用为止。 在内部,超时设置在 上,并最终在调用 上的发送操作时应用。 默认情况下,超时设置为“30”。 自选。send()MessagingTemplateMessageChannelsend()
10 布尔值,指示在将消息发送到请求通道以获取扩充数据之前是否应克隆实现的任何有效负载。 克隆版本将用作最终回复的目标有效载荷。 默认值为 。 自选。Cloneablefalse
11 如果此端点是轮询使用者,则允许您配置消息轮询器。 自选。
12 每个子元素都提供属性的名称(通过 mandatory 属性)。 该属性应可在目标负载实例上设置。 还必须提供其中一个 or 属性,前者用于设置文本值,后者用于计算 SpEL 表达式。 评估上下文的根对象是从此扩充器启动的流返回的消息 - 如果没有请求通道或应用程序上下文(使用 SpEL 语法)的输入消息。 从版本 4.0 开始,在指定属性时,还可以指定可选属性。 当目标是类型化 setter 方法时,框架会适当地强制该值(只要 )存在以处理转换。 但是,如果目标有效负载为 ,则该条目将填充该值而不进行转换。 例如,该属性允许您将包含数字的转换为目标有效负载中的值。 从版本 4.1 开始,您还可以指定可选属性。 当返回 null 时,将对其进行计算,并改为返回计算的输出。propertynamevalueexpression@<beanName>.<beanProperty>valuetypePropertyEditorMaptypeStringIntegernull-result-expressionenricher
13 每个子元素都提供消息标头的名称(通过 mandatory 属性)。 还必须提供其中一个 or 属性,前者用于设置文本值,后者用于计算 SpEL 表达式。 评估上下文的根对象是从此扩充器启动的流返回的消息 — 如果没有请求通道或应用程序上下文(使用“@<beanName>.<beanProperty>”SpEL 语法)的输入消息。 请注意,与 类似,元素的元素具有 和 属性。 但是,一个关键的区别是,默认情况下,该属性与元素的子元素一致。 从版本 4.1 开始,您还可以指定可选属性。 当返回 null 时,将对其进行计算,并改为返回计算的输出。headernamevalueexpression<header-enricher><enricher>headertypeoverwrite<enricher>overwritetrue<enricher><property>null-result-expressionenricher

例子

本节包含几个在各种情况下使用有效负载扩充器的示例。Spring中文文档

此处显示的代码示例是 Spring Integration Samples 项目的一部分。 请参阅 Spring 集成示例

在以下示例中,将对象作为 :UserMessageSpring中文文档

<int:enricher id="findUserEnricher"
              input-channel="findUserEnricherChannel"
              request-channel="findUserServiceChannel">
    <int:property name="email"    expression="payload.email"/>
    <int:property name="password" expression="payload.password"/>
</int:enricher>

具有多个属性,但最初仅设置了 。 扩充器的属性配置为将 传递给 .Userusernamerequest-channelUserfindUserServiceChannelSpring中文文档

通过隐式设置,将返回一个对象,并且通过使用子元素,从回复中提取属性并用于丰富原始有效负载。reply-channelUserpropertySpring中文文档

如何仅将一部分数据传递到请求通道?

使用属性时,可以将有效负载的单个属性(而不是完整消息)传递到请求通道。 在以下示例中,username 属性将传递到请求通道:request-payload-expressionSpring中文文档

<int:enricher id="findUserByUsernameEnricher"
              input-channel="findUserByUsernameEnricherChannel"
              request-channel="findUserByUsernameServiceChannel"
              request-payload-expression="payload.username">
    <int:property name="email"    expression="payload.email"/>
    <int:property name="password" expression="payload.password"/>
</int:enricher>

请记住,尽管只传递了用户名,但发送到请求通道的结果消息包含完整的 .MessageHeadersSpring中文文档

如何扩充由收集数据组成的有效负载?

在以下示例中,传入的不是对象,而是对象:UserMapSpring中文文档

<int:enricher id="findUserWithMapEnricher"
              input-channel="findUserWithMapEnricherChannel"
              request-channel="findUserByUsernameServiceChannel"
              request-payload-expression="payload.username">
    <int:property name="user" expression="payload"/>
</int:enricher>

包含映射键下的用户名。 只有 传递到请求通道。 回复包含一个完整的对象,该对象最终被添加到键下。MapusernameusernameUserMapuserSpring中文文档

如何在不使用请求通道的情况下使用静态信息丰富有效负载?

以下示例根本不使用请求通道,而只是使用静态值扩充消息的有效负载:Spring中文文档

<int:enricher id="userEnricher"
              input-channel="input">
    <int:property name="user.updateDate" expression="new java.util.Date()"/>
    <int:property name="user.firstName" value="William"/>
    <int:property name="user.lastName"  value="Shakespeare"/>
    <int:property name="user.age"       value="42"/>
</int:enricher>

请注意,“静态”一词在这里被松散地使用。 您仍然可以使用 SpEL 表达式来设置这些值。Spring中文文档

富集器是变压器的一种变体。 在许多情况下,您可以使用有效负载扩充器或通用转换器实现将其他数据添加到消息有效负载中。 您应该熟悉 Spring Integration 提供的所有支持转换的组件,并仔细选择语义上最适合您的业务案例的实现。
1 将消息发送到的通道以获取用于扩充的数据。 自选。
2 生命周期属性指示是否应在应用程序上下文启动期间启动此组件。 默认值为 true。 自选。
3 基础 Bean 定义的 ID,即 an 或 . 自选。EventDrivenConsumerPollingConsumer
4 指定此终结点作为订阅者连接到通道时的调用顺序。 当该通道使用“故障转移”调度策略时,这一点尤其重要。 当此终结点本身是具有队列的通道的轮询使用者时,它不起作用。 自选。
5 标识在此终结点处理消息后发送消息的消息通道。 自选。
6 默认情况下,原始消息的有效负载用作发送到 . 通过将 SpEL 表达式指定为属性的值,可以使用原始负载的子集、标头值或任何其他可解析的 SpEL 表达式作为发送到请求通道的负载的基础。 对于表达式计算,完整消息可作为“根对象”使用。 例如,以下 SpEL 表达式(以及其他表达式)是可能的:、、、request-channelrequest-payload-expressionpayload.somethingheaders.somethingnew java.util.Date()'thing1' + 'thing2'
7 预期会收到回复消息的频道。 这是可选的。 通常,自动生成的临时回复通道就足够了。 自选。
8 如果 an 发生在 的下游,则将 an 发送到的通道。 这使您能够返回用于扩充的替代对象。 如果未设置,则将向调用方抛出 an。 自选。ErrorMessageExceptionrequest-channelException
9 向通道发送消息时等待的最长时间(以毫秒为单位),如果通道可能阻塞。 例如,如果已达到队列通道的最大容量,则队列通道可以阻塞,直到空间可用为止。 在内部,超时设置在 上,并最终在调用 上的发送操作时应用。 默认情况下,超时设置为“30”。 自选。send()MessagingTemplateMessageChannelsend()
10 布尔值,指示在将消息发送到请求通道以获取扩充数据之前是否应克隆实现的任何有效负载。 克隆版本将用作最终回复的目标有效载荷。 默认值为 。 自选。Cloneablefalse
11 如果此端点是轮询使用者,则允许您配置消息轮询器。 自选。
12 每个子元素都提供属性的名称(通过 mandatory 属性)。 该属性应可在目标负载实例上设置。 还必须提供其中一个 or 属性,前者用于设置文本值,后者用于计算 SpEL 表达式。 评估上下文的根对象是从此扩充器启动的流返回的消息 - 如果没有请求通道或应用程序上下文(使用 SpEL 语法)的输入消息。 从版本 4.0 开始,在指定属性时,还可以指定可选属性。 当目标是类型化 setter 方法时,框架会适当地强制该值(只要 )存在以处理转换。 但是,如果目标有效负载为 ,则该条目将填充该值而不进行转换。 例如,该属性允许您将包含数字的转换为目标有效负载中的值。 从版本 4.1 开始,您还可以指定可选属性。 当返回 null 时,将对其进行计算,并改为返回计算的输出。propertynamevalueexpression@<beanName>.<beanProperty>valuetypePropertyEditorMaptypeStringIntegernull-result-expressionenricher
13 每个子元素都提供消息标头的名称(通过 mandatory 属性)。 还必须提供其中一个 or 属性,前者用于设置文本值,后者用于计算 SpEL 表达式。 评估上下文的根对象是从此扩充器启动的流返回的消息 — 如果没有请求通道或应用程序上下文(使用“@<beanName>.<beanProperty>”SpEL 语法)的输入消息。 请注意,与 类似,元素的元素具有 和 属性。 但是,一个关键的区别是,默认情况下,该属性与元素的子元素一致。 从版本 4.1 开始,您还可以指定可选属性。 当返回 null 时,将对其进行计算,并改为返回计算的输出。headernamevalueexpression<header-enricher><enricher>headertypeoverwrite<enricher>overwritetrue<enricher><property>null-result-expressionenricher
此处显示的代码示例是 Spring Integration Samples 项目的一部分。 请参阅 Spring 集成示例