此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Integration 6.5.1spring-doc.cadn.net.cn

内容丰富器

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

Spring 集成Core模块包括两个扩充器:spring-doc.cadn.net.cn

它还包括三个特定于适配器的标头扩充器:spring-doc.cadn.net.cn

请参阅本参考手册中特定于适配器的部分,以了解有关这些适配器的更多信息。spring-doc.cadn.net.cn

有关表达式支持的更多信息,请参阅 Spring 表达式语言 (SpEL)。spring-doc.cadn.net.cn

标题丰富器

如果您只需要向消息添加标头,并且标头不是由消息内容动态确定的,那么引用转换器的自定义实现可能有点矫枉过正。 因此,Spring Integration 提供了对标头扩充器模式的支持。 它通过<header-enricher>元素。 以下示例演示如何使用它:spring-doc.cadn.net.cn

<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-doc.cadn.net.cn

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

前面的配置显示,对于已知标头(例如errorChannel,correlationId,priority,replyChannel,routing-slip等),而不是使用泛型<header>子元素,您必须同时提供标头 'name' 和 'value',您可以使用方便的子元素直接设置这些值。spring-doc.cadn.net.cn

从 4.1 版开始,标头扩充器提供了一个routing-slip子元素。 有关更多信息,请参阅路由单spring-doc.cadn.net.cn

POJO 支持

通常,标头值不能静态定义,必须根据消息中的某些内容动态确定。 这就是为什么标头扩充器还允许您使用refmethod属性。 指定方法计算标头值。 考虑以下配置和一个 bean ,其方法修改了String:spring-doc.cadn.net.cn

<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-doc.cadn.net.cn

<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-doc.cadn.net.cn

<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) 的便利性,以帮助配置许多不同的组件。 标题富集器就是其中之一。 再看前面显示的 POJO 示例。 您可以看到确定标头值的计算逻辑非常简单。 一个自然的问题是:“有没有更简单的方法来实现这一目标? 这就是 SpEL 展示其真正力量的地方。 请考虑以下示例:spring-doc.cadn.net.cn

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

通过在此类简单情况下使用 SpEL,您不再需要提供单独的类并在应用程序上下文中对其进行配置。 您需要做的就是配置expression属性,并具有有效的 SpEL 表达式。 'payload' 和 'headers' 变量绑定到 SpEL 评估上下文,使您可以完全访问传入消息。spring-doc.cadn.net.cn

使用 Java 配置配置标头扩充器

以下两个示例显示了如何使用 Java 配置进行标头扩充器:spring-doc.cadn.net.cn

@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-doc.cadn.net.cn

使用 Java DSL 配置标头扩充器

以下示例显示了标头扩充器的 Java DSL 配置:spring-doc.cadn.net.cn

@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 开始,一个新的子元素<int:header-channels-to-string/>可用。 它没有属性。 这个新的子元素将现有的replyChannelerrorChannel标头(当它们是MessageChannel) 设置为String并将通道存储在注册表中,以便在需要发送回复或处理错误时进行解析。 这对于标头可能丢失的情况非常有用 - 例如,在将消息序列化到消息存储中或通过 JMS 传输消息时。 如果标头尚不存在,或者它不是MessageChannel,不进行任何更改。spring-doc.cadn.net.cn

使用此功能需要存在HeaderChannelRegistry豆。 默认情况下,框架会创建一个DefaultHeaderChannelRegistry默认到期时间(60 秒)。 在此时间之后,通道将从注册表中删除。 要更改此行为,请使用idintegrationHeaderChannelRegistry并使用构造函数参数(以毫秒为单位)配置所需的默认延迟。spring-doc.cadn.net.cn

从 4.1 版开始,您可以设置一个名为removeOnGettrue<bean/>定义,并且映射条目在首次使用时立即被删除。 这在大容量环境中以及当通道仅使用一次而不是等待收割器将其移除时可能很有用。spring-doc.cadn.net.cn

HeaderChannelRegistry有一个size()确定注册表当前大小的方法。 这runReaper()方法取消当前计划任务并立即运行 Reaper。 然后,根据当前延迟计划任务再次运行。 可以通过获取对注册表的引用直接调用这些方法,也可以将包含以下内容的消息发送到控制总线:spring-doc.cadn.net.cn

"integrationHeaderChannelRegistry.runReaper"

此子元素是一种方便,相当于指定以下配置:spring-doc.cadn.net.cn

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

从 V4.1 开始,您现在可以覆盖注册表配置的 reaper 延迟,以便通道映射至少保留指定时间,而不管 reaper 延迟如何。 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

<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-doc.cadn.net.cn

有效载荷丰富器

在某些情况下,如前所述,标头扩充器可能不够用,有效负载本身可能必须使用附加信息进行扩充。 例如,进入 Spring Integration 消息传递系统的订单消息必须根据提供的客户编号查找订单的客户,然后使用该信息丰富原始有效负载。spring-doc.cadn.net.cn

Spring Integration 2.1 引入了有效负载扩充器。 有效负载扩充器定义了一个端点,该端点将Message到公开的请求通道,然后需要回复消息。 然后,回复消息成为用于评估表达式以丰富目标有效负载的根对象。spring-doc.cadn.net.cn

有效负载扩充器通过enricher元素。 为了发送请求消息,有效负载扩充器具有request-channel属性,用于将消息分派到请求通道。spring-doc.cadn.net.cn

基本上,通过定义请求通道,有效负载扩充器充当网关,等待发送到请求通道的消息返回。 然后,扩充器使用回复消息提供的数据来增强消息的有效负载。spring-doc.cadn.net.cn

将消息发送到请求通道时,您还可以选择使用request-payload-expression属性。spring-doc.cadn.net.cn

有效载荷的丰富是通过 SpEL 表达式配置的,提供了最大程度的灵活性。 因此,您不仅可以使用回复通道的Message,但您可以使用 SpEL 表达式从该消息中提取子集或应用其他内联转换,从而进一步作数据。spring-doc.cadn.net.cn

如果您只需要使用静态值扩充有效负载,则无需提供request-channel属性。spring-doc.cadn.net.cn

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

配置

以下示例显示了有效负载扩充器的所有可用配置选项:spring-doc.cadn.net.cn

<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,即EventDrivenConsumerPollingConsumer. 自选。
4 指定此端点作为通道的订阅者连接到时的调用顺序。 当该通道使用“故障转移”调度策略时,这一点尤其重要。 当此端点本身是具有队列的通道的轮询使用者时,它不起作用。 自选。
5 标识在此终结点处理消息后发送消息的消息通道。 自选。
6 默认情况下,原始消息的有效负载用作发送到request-channel. 通过将 SpEL 表达式指定为request-payload-expression属性,您可以使用原始有效负载的子集、标头值或任何其他可解析的 SpEL 表达式作为发送到请求通道的有效负载的基础。 对于表达式评估,完整消息可用作“根对象”。 例如,以下 SpEL 表达式(以及其他)是可能的:payload.something,headers.something,new java.util.Date(),'thing1' + 'thing2'
7 需要回复消息的通道。 这是可选的。 通常,自动生成的临时回复通道就足够了。 自选。
8 一个ErrorMessage如果Exception发生在request-channel. 这使您能够返回用于扩充的替代对象。 如果未设置,则Exception抛给调用方。 自选。
9 如果通道可能阻塞,则向通道发送消息时等待的最长时间(以毫秒为单位)。 例如,如果队列通道已达到其最大容量,则队列通道可能会阻塞,直到空间可用。 在内部,send()timeout 在MessagingTemplate并最终在调用 send作时应用MessageChannel. 默认情况下,send()timeout 设置为“30”。 自选。
10 布尔值,指示是否实现Cloneable应在将消息发送到请求通道以获取扩充数据之前进行克隆。 克隆版本将用作最终回复的目标有效负载。 默认值为false. 自选。
11 如果此端点是轮询使用者,则允许您配置消息轮询器。 自选。
12 property子元素提供属性的名称(通过强制的name属性)。 该属性应该是目标有效负载实例上的可设置属性。 恰好是其中之一valueexpression还必须提供属性 - 前者用于设置要设置的文字值,后者用于要计算的 SpEL 表达式。 评估上下文的根对象是从此扩充器启动的流返回的消息 - 如果没有请求通道或应用程序上下文,则为输入消息(使用@<beanName>.<beanProperty>SpEL 语法)。 从 4.0 版开始,当指定value属性,您还可以指定可选的type属性。 当目标是类型化 setter 方法时,框架会适当地强制该值(只要PropertyEditor) 来处理转换。 但是,如果目标有效负载是Map,则条目将填充值而不进行转换。 这type属性允许您,例如,将String将数字包含在Integer目标有效负载中的值。 从 4.1 版开始,您还可以指定可选的null-result-expression属性。 当enricher返回 null,则对其进行评估,并返回评估的输出。
13 headersub元素提供消息头的名称(通过强制的name属性)。 恰好是其中之一valueexpression还必须提供属性——前者用于设置要设置的文字值,后者用于要计算的 SpEL 表达式。 评估上下文的根对象是从此扩充器启动的流返回的消息 - 如果没有请求通道或应用程序上下文(使用 '@<beanName>.<beanProperty>' SpEL 语法,则为输入消息)。 请注意,与<header-enricher><enricher>元素的header元素有typeoverwrite属性。 然而,一个关键的区别是,使用<enricher>overwrite属性为true默认情况下,要与<enricher>元素的<property>子元素。 从 4.1 版开始,您还可以指定可选的null-result-expression属性。 当enricher返回 null,则对其进行评估,并返回评估的输出。

例子

本节包含在各种情况下使用有效负载扩充器的几个示例。spring-doc.cadn.net.cn

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

在以下示例中,User对象作为Message:spring-doc.cadn.net.cn

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

User有多个属性,但只有username初始设置。 富集者的request-channel属性配置为传递UserfindUserServiceChannel.spring-doc.cadn.net.cn

通过隐式设置reply-channel一个User对象,并且通过使用property子元素,则从回复中提取属性并用于丰富原始有效负载。spring-doc.cadn.net.cn

如何仅将数据子集传递给请求通道?

使用request-payload-expression属性,则可以将有效负载的单个属性而不是完整消息传递到请求通道。 在以下示例中,username 属性被传递给请求通道:spring-doc.cadn.net.cn

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

请记住,尽管只传递了用户名,但发送到请求通道的生成消息包含完整的MessageHeaders.spring-doc.cadn.net.cn

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

在下面的示例中,而不是User对象,一个Map传入:spring-doc.cadn.net.cn

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

Map包含用户名,位于usernamemap 键。 只有username传递给请求通道。 回复包含完整的User对象,最终添加到Mapuser钥匙。spring-doc.cadn.net.cn

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

以下示例根本不使用请求通道,而仅使用静态值丰富消息的有效负载:spring-doc.cadn.net.cn

<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-doc.cadn.net.cn