Content Enricher
-
标头增强器:允许向消息添加新标头或动态设置现有标头。
-
有效负载增强器:将消息传递到一个请求通道,接收一个回复,并将回复中的数据用于扩充原始有效负载。
这些增强器可以通过 XML 名称空间或 Java 配置进行配置,并支持 SpEL 表达式、POJO 和 Groovy 脚本。
有时,您可能需要使用比目标系统提供的更多信息来增强请求。 data enricher模式描述了各种场景以及组件(Enricher),该组件可让您满足此类要求。
At times, you may have a requirement to enhance a request with more information than was provided by the target system. The data enricher pattern describes various scenarios as well as the component (Enricher) that lets you address such requirements.
Spring Integration“Core”模块包括两个增强器:
The Spring Integration Core
module includes two enrichers:
它还包括三个特定于适配器的标头增强器:
It also includes three adapter-specific header enrichers:
请参阅本参考手册的特定于适配器的部分,以了解有关这些适配器的更多信息。
See the adapter-specific sections of this reference manual to learn more about those adapters.
有关表达式支持的更多信息,请参阅 Spring Expression Language (SpEL)。
For more information regarding expressions support, see Spring Expression Language (SpEL).
Header Enricher
如果你不需要对消息添加标头,并且标头不会由消息内容动态确定,则引用转换器的自定义实现可能是矫枉过正。因此,Spring Integration 为标头增强器模式提供了支持。它是通过 <header-enricher>
元素公开的。以下示例展示了如何使用它:
If you need do nothing more than add headers to a message and the headers are not dynamically determined by the message content, referencing a custom implementation of a transformer may be overkill.
For that reason, Spring Integration provides support for the header enricher pattern.
It is exposed through the <header-enricher>
element.
The following example shows how to use it:
<int:header-enricher input-channel="in" output-channel="out">
<int:header name="foo" value="123"/>
<int:header name="bar" ref="someBean"/>
</int:header-enricher>
标头增强器还提供了有用的子元素来设置已知的标头名称,如下例所示:
The header enricher also provides helpful sub-elements to set well known header names, as the following example shows:
<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>
子元素(在其中需同时提供头部的“名称”和“值”),可使用便利的子元素直接设置这些值。
The preceding configuration shows that, for well known headers (such as errorChannel
, correlationId
, priority
, replyChannel
, routing-slip
, and others), instead of using generic <header>
sub-elements where you would have to provide both header 'name' and 'value', you can use convenient sub-elements to set those values directly.
从版本 4.1 开始,头增强器提供 routing-slip
子元素。有关详细信息,请参阅 Routing Slip。
Starting with version 4.1, the header enricher provides a routing-slip
sub-element.
See Routing Slip for more information.
POJO Support
通常,头部值无法静态定义,而必须基于消息中一些内容动态确定。这就是 header enricher 允许通过使用 ref
和 method
属性指定 bean 引用。指定的 method 计算头部值。考虑以下配置和一个通过 method 修改 String
的 bean:
Often, a header value cannot be defined statically and has to be determined dynamically based on some content in the message.
That is why the header enricher lets you also specify a bean reference by using the ref
and method
attributes.
The specified method calculates the header value.
Consider the following configuration and a bean with a method that modifies a 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,如下所示:
You can also configure your POJO as an inner bean, as the following example shows:
<int:header-enricher input-channel="inputChannel" output-channel="outputChannel">
<int:header name="some_header">
<bean class="org.MyEnricher"/>
</int:header>
</int:header-enricher>
您还可以指向 Groovy 脚本,如下面的示例所示:
You can similarly point to a Groovy script, as the following example shows:
<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 Support
在 Spring 集成 2.0 中,我们引入了 @{1} 的便利性,以帮助配置许多不同的组件。标头强化器就是其中之一。再看看前面所示的 POJO 示例。您可以看到用来确定标头值的计算逻辑非常简单。一个自然的问题是:“有更简单的方法来实现此目标吗?” SpEL 正是在这里展现了它的真正实力。请考虑以下示例:
In Spring Integration 2.0, we introduced the convenience of the Spring Expression Language (SpEL) to help configure many different components. The header enricher is one of them. Look again at the POJO example shown earlier. You can see that the computation logic to determine the header value is pretty simple. A natural question would be: "Is there an even simpler way to accomplish this?". That is where SpEL shows its true power. Consider the following example:
<int:header-enricher input-channel="in" output-channel="out">
<int:header name="foo" expression="payload.toUpperCase() + '_US'"/>
</int:header-enricher>
对这些简单案例使用 SpEL,您无需再提供单独的类并在应用程序上下文中配置它。您需要做的只是用有效的 SpEL 表达式配置 expression
属性。payload
和 headers
变量绑定到 SpEL 评估上下文中,让您完全访问传入的消息。
By using SpEL for such simple cases, you no longer have to provide a separate class and configure it in the application context.
All you need do is configured the expression
attribute with a valid SpEL expression.
The 'payload' and 'headers' variables are bound to the SpEL evaluation context, giving you full access to the incoming message.
Configuring a Header Enricher with Java Configuration
以下两个示例演示如何对 header enricher 使用 Java 配置:
The following two examples show how to use Java Configuration for header enrichers:
@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;
}
第一个示例添加了单个文字 header。第二个示例添加了两个 header,一个文字 header 和一个基于 SpEL 表达式的 header。
The first example adds a single literal header. The second example adds two headers, a literal header and one based on a SpEL expression.
Configuring a Header Enricher with the Java DSL
以下示例演示用于 header enricher 的 Java DSL 配置:
The following example shows Java DSL Configuration for a header enricher:
@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 开始,新的子元素 <int:header-channels-to-string/>
可用。它没有属性。此新子元素将现有的 replyChannel
和 errorChannel
header(当它们是 MessageChannel
时)转换为 String
,并将通道存储到注册表中以备稍后解决,也就是需要发送回复或处理错误时。在 header 可能丢失的情况下,这个很有用 —— 例如,当将消息序列化到消息存储或通过 JMS 传输消息时。如果 header 尚未存在,或者它不是 MessageChannel
,则不会进行任何更改。
Starting with Spring Integration 3.0, a new sub-element <int:header-channels-to-string/>
is available.
It has no attributes.
This new sub-element converts existing replyChannel
and errorChannel
headers (when they are a MessageChannel
) to a String
and stores the channels in a registry for later resolution, when it is time to send a reply or handle an error.
This is useful for cases where the headers might be lost — for example, when serializing a message into a message store or when transporting the message over JMS.
If the header does not already exist, or it is not a MessageChannel
, no changes are made.
使用此功能要求存在 HeaderChannelRegistry
bean。默认情况下,框架创建带有默认过期时间(60 秒)的 DefaultHeaderChannelRegistry
。此时间过后就从注册表中移除通道。要更改此行为,请使用构造函数参数(以毫秒为单位)定义一个 id
为 integrationHeaderChannelRegistry
的 bean,并配置所需的默认延迟。
Using this functionality requires the presence of a HeaderChannelRegistry
bean.
By default, the framework creates a DefaultHeaderChannelRegistry
with the default expiry (60 seconds).
Channels are removed from the registry after this time.
To change this behavior, define a bean with an id
of integrationHeaderChannelRegistry
and configure the required default delay by using a constructor argument (in milliseconds).
自 4.1 版以来,您可以在 <bean/>
定义中将名为 removeOnGet
的属性设置为 true
,并且在首次使用时立即删除映射条目。在海量环境中,并且只使用通道一次而不是等待清除程序将其删除时,这可能很有用。
Since version 4.1, you can set a property called removeOnGet
to true
on the <bean/>
definition, and the mapping entry is removed immediately on first use.
This might be useful in a high-volume environment and when the channel is only used once, rather than waiting for the reaper to remove it.
HeaderChannelRegistry
具有 size()
方法来确定注册表的当前大小。runReaper()
方法取消当前计划的任务并立即运行清除程序。然后基于当前延迟重新计划任务来运行。可以通过获取对注册表的引用直接调用这些方法,或者也可以向控制总线发送消息(例如,其内容如下):
The HeaderChannelRegistry
has a size()
method to determine the current size of the registry.
The runReaper()
method cancels the current scheduled task and runs the reaper immediately.
The task is then scheduled to run again based on the current delay.
These methods can be invoked directly by getting a reference to the registry, or you can send a message with, for example, the following content to a control bus:
"@integrationHeaderChannelRegistry.runReaper()"
此子元素是一个便利的元素,相当于指定以下配置:
This sub-element is a convenience, and is the equivalent of specifying the following configuration:
<int:reply-channel
expression="@integrationHeaderChannelRegistry.channelToChannelName(headers.replyChannel)"
overwrite="true" />
<int:error-channel
expression="@integrationHeaderChannelRegistry.channelToChannelName(headers.errorChannel)"
overwrite="true" />
从 4.1 版开始,您现在可以覆盖注册表已配置的清除程序延迟,以便无论清除程序延迟如何,通道映射都至少保留指定的时间。以下示例演示如何操作:
Starting with version 4.1, you can now override the registry’s configured reaper delay so that the channel mapping is retained for at least the specified time, regardless of the reaper delay. The following example shows how to do so:
<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>
在第一种情况下,每个 header 通道映射的生存时间为两分钟。在第二种情况下,生存时间在消息 header 中指定,并利用 Elvis 运算符在没有 header 的情况下使用两分钟。
In the first case, the time to live for every header channel mapping will be two minutes. In the second case, the time to live is specified in the message header and uses an Elvis operator to use two minutes if there is no header.
Payload Enricher
在某些情况下,前面讨论的 header enricher 可能不够,而 payload 本身可能需要使用其他信息来充实。例如,进入 Spring Integration 消息传送系统的订单消息必须根据提供的客户编号查找订单的客户,然后使用这些信息充实原始 payload。
In certain situations, the header enricher, as discussed earlier, may not be sufficient and payloads themselves may have to be enriched with additional information. For example, order messages that enter the Spring Integration messaging system have to look up the order’s customer based on the provided customer number and then enrich the original payload with that information.
Spring Integration 2.1 引入了 payload enricher。payload enricher 定义了一个端点,将 Message
传递到公开的请求通道,然后它预计收到一个回复消息。然后回复消息会成为评估表达式以充实目标 payload 的根对象。
Spring Integration 2.1 introduced the payload enricher.
The payload enricher defines an endpoint that passes a Message
to the exposed request channel and then expects a reply message.
The reply message then becomes the root object for evaluation of expressions to enrich the target payload.
有效负载增强器通过 enricher
元素提供完整的 XML 名称空间支持。为了发送请求消息,有效负载增强器有一个 request-channel
属性,便于将消息分派到请求通道。
The payload enricher provides full XML namespace support through the enricher
element.
In order to send request messages, the payload enricher has a request-channel
attribute that lets you dispatch messages to a request channel.
基本上,通过定义请求通道,有效负载增强器充当网关,等待发送到请求通道的消息返回。然后,该增强器会根据回复消息提供的数据扩充消息的有效负载。
Basically, by defining the request channel, the payload enricher acts as a gateway, waiting for the message sent to the request channel to return. The enricher then augments the message’s payload with the data provided by the reply message.
在向请求通道发送消息时,您还可以仅通过使用 request-payload-expression
属性发送原始有效负载的子集。
When sending messages to the request channel, you also have the option to send only a subset of the original payload by using the request-payload-expression
attribute.
有效负载的扩充是通过 SpEL 表达式配置的,提供了最大程度的灵活性。因此,您不仅可以用来自回复通道的 Message
的直接值扩充有效负载,还可以使用 SpEL 表达式从该消息中提取子集或应用其他内联转换,从而进一步处理数据。
The enriching of payloads is configured through SpEL expressions, providing a maximum degree of flexibility.
Therefore, you can not only enrich payloads with direct values from the reply channel’s Message
, but you can use SpEL expressions to extract a subset from that message or to apply additional inline transformations, letting you further manipulate the data.
如果您只需要用静态值扩充有效负载,则无需提供 request-channel
属性。
If you need only to enrich payloads with static values, you need not provide the request-channel
attribute.
丰富器是转换器的一种变体。在许多情况下,可以使用有效负载丰富器或通用转换器实现来向你的消息有效负载中添加额外数据。你应熟悉 Spring Integration 提供的所有转换功能组件,并仔细选择在语义上最适合你的业务案例的实现。 |
Enrichers are a variant of transformers. In many cases, you could use a payload enricher or a generic transformer implementation to add additional data to your message payloads. You should familiarize yourself with all transformation-capable components that are provided by Spring Integration and carefully select the implementation that semantically fits your business case best. |
Configuration
以下示例显示了有效负载增强器的所有可用配置选项:
The following example shows all available configuration options for the payload enricher:
<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 | Channel to which a message is sent to get the data to use for enrichment. Optional. |
2 | Lifecycle attribute signaling whether this component should be started during the application context startup. Defaults to true. Optional. |
3 | ID of the underlying bean definition, which is either an EventDrivenConsumer or a PollingConsumer .
Optional. |
4 | Specifies the order for invocation when this endpoint is connected as a subscriber to a channel. This is particularly relevant when that channel is using a “failover” dispatching strategy. It has no effect when this endpoint is itself a polling consumer for a channel with a queue. Optional. |
5 | Identifies the message channel where a message is sent after it is being processed by this endpoint. Optional. |
6 | By default, the original message’s payload is used as payload that is sent to the request-channel .
By specifying a SpEL expression as the value for the request-payload-expression attribute, you can use a subset of the original payload, a header value, or any other resolvable SpEL expression as the basis for the payload that is sent to the request-channel.
For the expression evaluation, the full message is available as the 'root object'.
For instance, the following SpEL expressions (among others) are possible: payload.something , headers.something , new java.util.Date() , ’thing1' + 'thing2'` |
7 | Channel where a reply message is expected. This is optional. Typically, the auto-generated temporary reply channel suffices. Optional. |
8 | The channel to which an ErrorMessage is sent if an Exception occurs downstream of the request-channel .
This enables you to return an alternative object to use for enrichment.
If it is not set, an Exception is thrown to the caller.
Optional. |
9 | Maximum amount of time in milliseconds to wait when sending a message to the channel, if the channel might block.
For example, a queue channel can block until space is available, if its maximum capacity has been reached.
Internally, the send() timeout is set on the MessagingTemplate and ultimately applied when invoking the send operation on the MessageChannel .
By default, the send() timeout is set to '30'.
Optional. |
10 | Boolean value indicating whether any payload that implements Cloneable should be cloned prior to sending the message to the request channel for acquiring the enriching data.
The cloned version would be used as the target payload for the ultimate reply.
The default is false .
Optional. |
11 | Lets you configure a message poller if this endpoint is a polling consumer. Optional. |
12 | Each property sub-element provides the name of a property (through the mandatory name attribute).
That property should be settable on the target payload instance.
Exactly one of the value or expression attributes must be provided as well — the former for a literal value to set and the latter for a SpEL expression to be evaluated.
The root object of the evaluation context is the message that was returned from the flow initiated by this enricher — the input message if there is no request channel or the application context (using the @<beanName>.<beanProperty> SpEL syntax).
Starting with version 4.0, when specifying a value attribute, you can also specify an optional type attribute.
When the destination is a typed setter method, the framework coerces the value appropriately (as long as a PropertyEditor ) exists to handle the conversion.
If, however, the target payload is a Map , the entry is populated with the value without conversion.
The type attribute lets you, for example, convert a String containing a number to an Integer value in the target payload.
Starting with version 4.1, you can also specify an optional null-result-expression attribute.
When the enricher returns null, it is evaluated, and the output of the evaluation is returned instead. |
13 | Each header sub-element provides the name of a message header (through the mandatory name attribute).
Exactly one of the value or expression attributes must also be provided — the former for a literal value to set and the latter for a SpEL expression to be evaluated.
The root object of the evaluation context is the message that was returned from the flow initiated by this enricher — the input message if there is no request channel or the application context (using the '@<beanName>.<beanProperty>' SpEL syntax).
Note that, similarly to the <header-enricher> , the <enricher> element’s header element has type and overwrite attributes.
However, a key difference is that, with the <enricher> , the overwrite attribute is true by default, to be consistent with the <enricher> element’s <property> sub-element.
Starting with version 4.1, you can also specify an optional null-result-expression attribute.
When the enricher returns null, it is evaluated, and the output of the evaluation is returned instead. |
Examples
此部分包含有关在各种情况下使用有效负载增强器的几个示例。
This section contains several examples of using a payload enricher in various situations.
这里显示的代码示例是 Spring Integration Samples 项目的一部分。请参阅 Spring Integration Samples。 |
The code samples shown here are part of the Spring Integration Samples project. See Spring Integration Samples. |
在以下示例中,将 User
对象作为 Message
的有效负载传递:
In the following example, a User
object is passed as the payload of the 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>
User
有几个属性,但最初只设置了 username
。增强器的 request-channel
属性配置为将 User
传递到 findUserServiceChannel
。
The User
has several properties, but only the username
is set initially.
The enricher’s request-channel
attribute is configured to pass the User
to the findUserServiceChannel
.
通过隐式设置的 reply-channel
,返回一个 User
对象,并通过使用 property
子元素,从回复中提取属性并用于扩充原始有效负载。
Through the implicitly set reply-channel
, a User
object is returned and, by using the property
sub-element, properties from the reply are extracted and used to enrich the original payload.
How Do I Pass Only a Subset of Data to the Request Channel?
在使用 request-payload-expression
属性时,可以将有效负载的单个属性(而不是全部消息)传递到请求通道。在以下示例中,将 username 属性传递到请求通道:
When using a request-payload-expression
attribute, a single property of the payload instead of the full message can be passed on to the request channel.
In the following example, the username property is passed on to the request channel:
<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>
请记住,尽管只传递了 username,但发送到请求通道的结果消息包含全部 MessageHeaders
。
Keep in mind that, although only the username is passed, the resulting message to the request channel contains the full set of MessageHeaders
.
How Can I Enrich Payloads that Consist of Collection Data?
在以下示例中,不是 User
对象,而是一个 Map
被传递进来:
In the following example, instead of a User
object, a Map
is passed in:
<int:enricher id="findUserWithMapEnricher"
input-channel="findUserWithMapEnricherChannel"
request-channel="findUserByUsernameServiceChannel"
request-payload-expression="payload.username">
<int:property name="user" expression="payload"/>
</int:enricher>
该 Map
包含 map 键 username
下的 username。只有 username
被传递到请求通道。回复包含一个完整的 User
对象,最终该对象已添加到键 user
下的 Map
中。
The Map
contains the username under the username
map key.
Only the username
is passed on to the request channel.
The reply contains a full User
object, which is ultimately added to the Map
under the user
key.
How Can I Enrich Payloads with Static Information without Using a Request Channel?
以下示例根本不使用请求通道,而只用静态值扩充消息的有效负载:
The following example does not use a request channel at all but solely enriches the message’s payload with static values:
<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 表达式来设置这些值。
Note that the word, 'static', is used loosely here. You can still use SpEL expressions for setting those values.