对于最新的稳定版本,请使用 Spring Integration 6.4.0! |
内容丰富器
有时,您可能需要使用比目标系统提供的信息更多的信息来增强请求。 数据扩充器模式描述了各种方案以及允许您满足此类需求的组件 (Enricher)。
Spring 集成模块包括两个丰富器:Core
它还包括三个特定于适配器的标头扩充器:
请参阅本参考手册中特定于适配器的部分,以了解有关这些适配器的更多信息。
有关表达式支持的更多信息,请参阅 Spring 表达式语言 (SpEL)。
标头扩充器
如果您只需要向消息添加 Headers,并且 Headers 不是由消息内容动态确定的,则引用转换器的自定义实现可能有点矫枉过正。
出于这个原因, Spring 集成提供了对 Headers Enricher 模式的支持。
它通过元素公开。
以下示例演示如何使用它:<header-enricher>
<int:header-enricher input-channel="in" output-channel="out">
<int:header name="foo" value="123"/>
<int:header name="bar" ref="someBean"/>
</int:header-enricher>
Header Enricher 还提供了有用的子元素来设置众所周知的 Headers 名称,如下例所示:
<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>
前面的配置表明,对于众所周知的 Headers(例如 , , , , 等),您可以使用方便的子元素直接设置这些值,而不是使用必须同时提供 Headers 'name' 和 'value' 的通用子元素。errorChannel
correlationId
priority
replyChannel
routing-slip
<header>
从版本 4.1 开始,Header Enricher 提供了一个 sub-element。
有关更多信息,请参阅路由单。routing-slip
POJO 支持
通常,标头值不能静态定义,必须根据消息中的某些内容动态确定。
这就是为什么 Header Enricher 还允许您使用 and 属性指定 bean 引用的原因。
指定的方法计算 header 值。
考虑以下配置和一个 bean,其方法修改了:ref
method
String
<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,如下例所示:
<int:header-enricher input-channel="inputChannel" output-channel="outputChannel">
<int:header name="some_header">
<bean class="org.MyEnricher"/>
</int:header>
</int:header-enricher>
同样,您可以指向 Groovy 脚本,如下例所示:
<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) 的便利性,以帮助配置许多不同的组件。 header enricher 就是其中之一。 再看一下前面显示的 POJO 示例。 你可以看到,确定 header 值的计算逻辑非常简单。 一个自然而然的问题是:“有没有更简单的方法可以做到这一点? 这就是 SpEL 展示其真正力量的地方。 请考虑以下示例:
<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 评估上下文,为您提供对传入消息的完全访问权限。expression
使用 Java 配置配置 Header Enricher
以下两个示例显示了如何将 Java 配置用于 Headers 扩充器:
@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;
}
第一个示例添加单个文本标头。 第二个示例添加了两个 Headers,一个 Literal Headers 和一个基于 SpEL 表达式。
使用 Java DSL 配置头文件丰富器
以下示例显示了标头扩充器的 Java DSL 配置:
@Bean
public IntegrationFlow enrichHeadersInFlow() {
return f -> f
...
.enrichHeaders(h -> h.header("emailUrl", this.emailUrl)
.headerExpression("from", "payload.from[0].toString()"))
.handle(...);
}
Header Channel Registry
从 Spring Integration 3.0 开始,可以使用一个新的子元素。
它没有属性。
这个新的子元素将 existing 和 headers(当它们是 a 时)转换为 a ,并将通道存储在注册表中,以便以后在发送回复或处理错误时解决。
这在标头可能丢失的情况下非常有用,例如,在将消息序列化到消息存储中或通过 JMS 传输消息时。
如果报头尚不存在,或者它不是 ,则不会进行任何更改。<int:header-channels-to-string/>
replyChannel
errorChannel
MessageChannel
String
MessageChannel
使用此功能需要存在 Bean。
默认情况下,框架会创建一个具有默认过期时间(60 秒)的 。
在此时间之后,将从注册表中删除通道。
要更改此行为,请使用 of 定义一个 bean,并使用 constructor 参数(以毫秒为单位)配置所需的默认延迟。HeaderChannelRegistry
DefaultHeaderChannelRegistry
id
integrationHeaderChannelRegistry
从版本 4.1 开始,您可以在定义上设置一个名为 to 的属性,并且 mapping 条目在首次使用时会立即删除。
这在高容量环境中以及当 channel 只使用一次时可能很有用,而不是等待 reaper 将其删除。removeOnGet
true
<bean/>
具有确定注册表当前大小的方法。
该方法取消当前计划任务并立即运行 reaper。
然后,根据当前延迟计划任务再次运行。
可以通过获取对注册表的引用来直接调用这些方法,也可以将包含以下内容的消息发送到控制总线:HeaderChannelRegistry
size()
runReaper()
"@integrationHeaderChannelRegistry.runReaper()"
此 sub-element 是一种方便,相当于指定以下配置:
<int:reply-channel
expression="@integrationHeaderChannelRegistry.channelToChannelName(headers.replyChannel)"
overwrite="true" />
<int:error-channel
expression="@integrationHeaderChannelRegistry.channelToChannelName(headers.errorChannel)"
overwrite="true" />
从版本 4.1 开始,您现在可以覆盖 registry 配置的 reaper delay,以便 channel mapping 至少保留指定的时间,而不管 reaper 延迟如何。 以下示例显示了如何执行此操作:
<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>
在第一种情况下,每个标头通道映射的生存时间为 2 分钟。 在第二种情况下,生存时间在消息报头中指定,如果没有报头,则使用 Elvis 运算符使用两分钟。
Payload Enricher
在某些情况下,如前所述,标头扩充器可能不够,有效负载本身可能必须使用其他信息进行扩充。 例如,进入 Spring Integration 消息传递系统的订单消息必须根据提供的客户编号查找订单的客户,然后用该信息丰富原始有效负载。
Spring Integration 2.1 引入了有效负载丰富器。
有效负载扩充器定义一个终端节点,该终端节点将 a 传递给公开的请求通道,然后需要回复消息。
然后,回复消息成为用于评估表达式以丰富目标负载的根对象。Message
有效负载扩充器通过元素提供完整的 XML 命名空间支持。
为了发送请求消息,有效负载扩充器具有一个属性,该属性允许您将消息分派到请求通道。enricher
request-channel
基本上,通过定义请求通道,有效负载丰富器充当网关,等待发送到请求通道的消息返回。 然后,扩充器使用回复消息提供的数据来扩充消息的有效负载。
在向请求通道发送消息时,您还可以选择使用 属性仅发送原始负载的子集。request-payload-expression
有效负载的丰富是通过 SPEL 表达式配置的,从而提供了最大程度的灵活性。
因此,您不仅可以使用回复通道中的直接值来丰富有效负载,还可以使用 SPEL 表达式从该消息中提取子集或应用其他内联转换,从而进一步操作数据。Message
如果只需要使用静态值扩充有效负载,则无需提供该属性。request-channel
Enrichers 是 transformer 的一种变体。 在许多情况下,您可以使用有效负载扩充器或通用转换器实现将其他数据添加到消息有效负载中。 你应该熟悉 Spring Integration 提供的所有支持转换的组件,并仔细选择在语义上最适合你的业务案例的实现。 |
配置
以下示例显示了有效负载扩充器的所有可用配置选项:
<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 或 a 。
自选。EventDrivenConsumer PollingConsumer |
4 | 指定此终端节点作为订阅者连接到通道时的调用顺序。 当该通道使用 “failover” 分派策略时,这一点尤其重要。 当此终端节点本身是具有队列的通道的轮询使用者时,它不起作用。 自选。 |
5 | 标识此终端节点处理消息后将消息发送到的消息通道。 自选。 |
6 | 默认情况下,原始消息的有效负载用作发送到 的有效负载。
通过将 SPEL 表达式指定为属性的值,您可以使用原始有效负载的子集、标头值或任何其他可解析的 SPEL 表达式作为发送到请求通道的有效负载的基础。
对于表达式计算,完整消息可用作 'root object'。
例如,可以使用以下 SpEL 表达式(以及其他表达式):、、、request-channel request-payload-expression payload.something headers.something new java.util.Date() 'thing1' + 'thing2' |
7 | 需要回复消息的频道。 这是可选的。 通常,自动生成的临时回复通道就足够了。 自选。 |
8 | 如果 发生在 的下游,则将 an 发送到的通道。
这使您能够返回用于扩充的替代对象。
如果未设置,则向调用方抛出 an。
自选。ErrorMessage Exception request-channel Exception |
9 | 如果通道可能阻塞,则向通道发送消息时要等待的最长时间(以毫秒为单位)。
例如,如果已达到其最大容量,则队列通道可以阻塞,直到有可用空间为止。
在内部,超时在 上设置,并最终在 上调用 send 操作时应用 。
默认情况下,超时设置为 '30'。
自选。send() MessagingTemplate MessageChannel send() |
10 | 布尔值,指示在将消息发送到请求通道以获取丰富数据之前,是否应克隆任何实现的有效负载。
克隆的版本将用作最终回复的目标负载。
默认值为 .
自选。Cloneable false |
11 | 允许您配置消息轮询器(如果此端点是轮询使用者)。 自选。 |
12 | 每个子元素都提供属性的名称(通过 mandatory 属性)。
该属性应在目标负载实例上设置。
还必须提供 or 属性之一——前者用于设置文字值,后者用于要求值的 SPEL 表达式。
评估上下文的根对象是从此扩充器启动的流返回的消息 — 如果没有请求通道或应用程序上下文(使用 SPEL 语法),则为 input 消息。
从版本 4.0 开始,在指定属性时,您还可以指定 optional 属性。
当目标是类型化 setter 方法时,框架会适当地强制值(只要 存在 )以处理转换。
但是,如果目标有效负载是 a ,则条目中填充值而不进行转化。
例如,该属性允许您将包含数字的 a 转换为目标有效负载中的值。
从版本 4.1 开始,您还可以指定 optional 属性。
当返回 null 时,将对其进行评估,并返回评估的输出。property name value expression @<beanName>.<beanProperty> value type PropertyEditor Map type String Integer null-result-expression enricher |
13 | 每个子元素都提供消息头的名称(通过 mandatory 属性)。
还必须提供 or 属性之一——前者用于设置文字值,后者用于要求值的 SPEL 表达式。
评估上下文的根对象是从此丰富器启动的流返回的消息 — 如果没有请求通道或应用程序上下文(使用 '@<beanName>.<beanProperty>' SPEL 语法,则为输入消息)。
请注意,与 类似,元素的元素具有 和 属性。
但是,一个关键的区别是,默认情况下,使用 , 该属性与元素的子元素一致。
从版本 4.1 开始,您还可以指定 optional 属性。
当返回 null 时,将对其进行评估,并返回评估的输出。header name value expression <header-enricher> <enricher> header type overwrite <enricher> overwrite true <enricher> <property> null-result-expression enricher |
例子
本节包含在各种情况下使用有效负载扩充器的几个示例。
此处显示的代码示例是 Spring 集成示例项目的一部分。 参见 Spring 集成示例。 |
在以下示例中,对象作为 的有效负载传递:User
Message
<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>
this 具有多个属性,但最初仅设置 the 。
enricher 的属性配置为将 传递给 .User
username
request-channel
User
findUserServiceChannel
通过 implicitly set ,返回一个对象,并使用 sub-element 提取回复中的属性,并将其用于丰富原始有效载荷。reply-channel
User
property
如何仅将数据子集传递给请求通道?
使用属性时,可以将有效负载的单个属性而不是完整消息传递给请求通道。
在以下示例中,将 username 属性传递给请求通道:request-payload-expression
<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
如何丰富包含集合数据的有效负载?
在下面的示例中,传入的不是 object,而是 a:User
Map
<int:enricher id="findUserWithMapEnricher"
input-channel="findUserWithMapEnricherChannel"
request-channel="findUserByUsernameServiceChannel"
request-payload-expression="payload.username">
<int:property name="user" expression="payload"/>
</int:enricher>
该 包含 map 键下的用户名。
只有 the 被传递到请求通道。
回复包含一个完整的对象,该对象最终被添加到 key 下。Map
username
username
User
Map
user
如何在不使用请求通道的情况下使用静态信息丰富有效负载?
以下示例根本不使用请求通道,而仅使用静态值丰富消息的有效负载:
<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>
请注意,“static”一词在这里使用得很松散。 您仍然可以使用 SPEL 表达式来设置这些值。