Spring Integration (集成)消息转换_序列化

变压器

消息转换器在实现消息生产者和消息使用者的松散耦合方面起着非常重要的作用。 无需要求每个消息生成组件都知道下一个使用者期望的类型,您可以在这些组件之间添加转换器。 通用转换器(例如将 a 转换为 XML 文档的转换器)也是高度可重用的。​​String​

对于某些系统,最好提供一个规范的数据模型,但Spring Integration的一般理念是不需要任何特定的格式。 相反,为了获得最大的灵活性,Spring Integration旨在提供最简单的扩展模型。 与其他端点类型一样,在 XML 或 Java 注释中使用声明性配置使简单的 POJO 能够适应消息转换器的角色。 本章的其余部分将介绍这些配置选项。

为了最大限度地提高灵活性,Spring 不需要基于 XML 的消息有效负载。 尽管如此,该框架确实提供了一些方便的转换器来处理基于 XML 的有效负载,如果这确实是应用程序的正确选择。 有关这些转换器的详细信息,请参阅 XML支持 - 处理 XML 有效负载。

使用 XML 配置转换器

该元素用于创建消息转换终结点。 除了 andattributes 之外,它还需要属性。 可以指向包含单个方法注释的对象(请参阅使用注释配置转换器),也可以与属性中提供的显式方法名称值组合。​​<transformer>​​​​input-channel​​​​output-channel​​​​ref​​​​ref​​​​@Transformer​​​​method​

<int:transformer id="testTransformer" ref="testTransformerBean" input-channel="inChannel"
method="transform" output-channel="outChannel"/>
<beans:bean id="testTransformerBean" class="org.foo.TestTransformer" />

如果自定义转换器处理程序实现可以在其他定义中重用,则通常建议使用 aattribute。 但是,如果自定义转换器处理程序实现的范围应限定为 的单个定义,则可以定义内部 Bean 定义,如以下示例所示:​​ref​​​​<transformer>​​​​<transformer>​

<int:transformer id="testTransformer" input-channel="inChannel" method="transform"
output-channel="outChannel">
<beans:bean class="org.foo.TestTransformer"/>
</transformer>

不允许在同一配置中同时使用 theattribute 和内部处理程序定义,因为它会产生不明确的条件并导致引发异常。​​ref​​​​<transformer>​

如果属性引用扩展的 bean(例如框架本身提供的转换器),则通过将输出通道直接注入处理程序来优化配置。 在这种情况下,每个都必须是单独的 Bean 实例(或 a-scope Bean)或使用内部配置类型。 如果无意中从多个 Bean 引用了相同的消息处理程序,则会出现配置异常。​​ref​​​​AbstractMessageProducingHandler​​​​ref​​​​prototype​​​​<bean/>​

使用 POJO 时,用于转换的方法可能需要入站消息的类型或有效负载类型。 它还可以单独接受消息头值,也可以分别使用 andparameter 注释作为完整映射接受消息头值。 该方法的返回值可以是任何类型。 如果返回值本身为 a,则传递到变压器的输出通道。​​Message​​​​@Header​​​​@Headers​​​​Message​

从 Spring Integration 2.0 开始,消息转换器的转换方法无法再返回。 返回会导致异常,因为消息转换器应始终期望将每个源消息转换为有效的目标消息。 换句话说,消息转换器不应用作消息筛选器,因为有一个专用选项。 但是,如果您确实需要这种类型的行为(组件可能会返回并且不应被视为错误),则可以使用服务激活器。 默认情况下,itsvalue 是,但可以将其设置为为了为返回值引发异常,就像转换器一样。​​null​​​​null​​​​<filter>​​​​null​​​​requires-reply​​​​false​​​​true​​​​null​

Transformers and Spring Expression Language (SpEL)

与路由器、聚合器和其他组件一样,从 Spring Integration 2.0 开始,只要转换逻辑相对简单,转换器也可以从SpEL 支持中受益。 以下示例演示如何使用 SpEL 表达式:

<int:transformer input-channel="inChannel"
output-channel="outChannel"
expression="payload.toUpperCase() + '- [' + T(System).currentTimeMillis() + ']'"/>

前面的示例在不编写自定义转换器的情况下转换有效负载。 我们的有效负载(假设为 a)是大写的,与当前时间戳连接,并应用了一些格式。​​String​

普通变压器

Spring Integration提供了一些转换器实现。

对象到串变压器

因为使用其中表示是相当普遍的,Spring Integration提供了一个输出是带有字符串的。 这是对入站消息的有效负载调用操作的结果。 下面的示例演示如何声明对象到字符串转换器的实例:​​toString()​​​​Object​​​​ObjectToStringTransformer​​​​Message​​​​payload​​​​String​​​​toString()​

<int:object-to-string-transformer input-channel="in" output-channel="out"/>

此转换器的潜在用途是将一些任意对象发送到命名空间中的“出站通道适配器”。 而该通道适配器默认情况下仅支持字节数组或有效负载,在适配器处理必要的转换之前立即添加此转换器。 只要调用的结果是您想要写入文件的结果,就可以正常工作。 否则,可以使用前面显示的通用“转换器”元素提供基于 POJO 的自定义转换器。​​file​​​​String​​​​java.io.File​​​​toString()​

调试时,通常不需要此转换器,因为它能够记录消息有效负载。 有关更多详细信息,请参阅丝锥​。​​logging-channel-adapter​


对象到字符串转换器非常简单。 它调用入站有效负载。 从 Spring Integration 3.0 开始,此规则有两个例外:​​toString()​



  • 如果有效负载为 a,则调用。​​char[]​​​​new String(payload)​
  • 如果有效负载为 a,则调用,其中默认为 UTF-8。 可以通过在转换器上提供字符集属性来修改。​​byte[]​​​​new String(payload, charset)​​​​charset​​​​charset​



对于更复杂的方法(例如在运行时动态选择字符集),可以改用基于 SpEL 表达式的转换器,如以下示例所示:




<int:transformer input-channel="in" output-channel="out"
expression="new java.lang.String(payload, headers['myCharset']" />



如果您需要序列化字节数组或将字节数组反序列化回字节数组,Spring Integration 提供了对称的序列化转换器。 默认情况下,它们使用标准 Java 序列化,但您可以分别使用 theandattributes 提供 Springorstrategies 的实现。 以下示例显示了如何使用 Spring 的序列化程序和反序列化器:​​Object​​​​Object​​​​Serializer​​​​Deserializer​​​​serializer​​​​deserializer​

<int:payload-serializing-transformer input-channel="objectsIn" output-channel="bytesOut"/>

<int:payload-deserializing-transformer input-channel="bytesIn" output-channel="objectsOut"
allow-list="com.mycom.*,com.yourcom.*"/>

从不受信任的源反序列化数据时,应考虑添加 aof 包和类模式。 默认情况下,所有类都是反序列化的。​​allow-list​

​Object​​-到变压器和到变压器​​Map​​​​Map​​​​Object​

Spring Integration 还提供了 to-and-to-transformer,它们使用 JSON 来序列化和反序列化对象图。 对象层次结构被自省为最原始的类型(、等)。 这种类型的路径用 SpEL 描述,它成为转换后的 the。 基元类型成为值。​​Object​​​​Map​​​​Map​​​​Object​​​​String​​​​int​​​​key​​​​Map​

请考虑以下示例:

public class Parent{
private Child child;
private String name;
// setters and getters are omitted
}

public class Child{
private String name;
private List<String> nickNames;
// setters and getters are omitted
}

前面示例中的两个类将转换为以下内容:​​Map​

{person.name=George, person.child.name=Jenna, person.child.nickNames[0]=Jen ...}

基于 JSON 允许您在不共享实际类型的情况下描述对象结构,只要您保持结构,您就可以将对象图还原并重建为不同类型的对象图。​​Map​

例如,可以使用 to-transformer 将上述结构恢复回以下对象图:​​Map​​​​Object​

public class Father {
private Kid child;
private String name;
// setters and getters are omitted
}

public class Kid {
private String name;
private List<String> nickNames;
// setters and getters are omitted
}

如果需要创建“结构化”映射,可以提供属性。 默认值为“true”。 如果将其设置为“false”,则结构为 aofobjects。​​flatten​​​​Map​​​​Map​

请考虑以下示例:

public class Parent {
private Child child;
private String name;
// setters and getters are omitted
}

public class Child {
private String name;
private List<String> nickNames;
// setters and getters are omitted
}

前面示例中的两个类将转换为以下内容:​​Map​

{name=George, child={name=Jenna, nickNames=[Bimbo, ...]}}

为了配置这些转换器,Spring Integration 为对象到映射提供了命名空间支持,如以下示例所示:

<int:object-to-map-transformer input-channel="directInput" output-channel="output"/>

您还可以将属性设置为 false,如下所示:​​flatten​

<int:object-to-map-transformer input-channel="directInput" output-channel="output" flatten="false"/>

Spring 集成为映射到对象提供了命名空间支持,如以下示例所示:

<int:map-to-object-transformer input-channel="input"
output-channel="output"
type="org.something.Person"/>

或者,您可以使用 aattribute 和原型范围的 Bean,如以下示例所示:​​ref​

<int:map-to-object-transformer input-channel="inputA"
output-channel="outputA"
ref="person"/>
<bean id="person" class="org.something.Person" scope="prototype"/>

“ref”和“type”属性是互斥的。 此外,如果使用“ref”属性,则必须指向“原型”作用域的 Bean。 否则,ais 被抛出。​​BeanCreationException​

从版本 5.0 开始,您可以提供自定义的 — 当您需要特殊格式的日期或空集合(和其他用途)的空值时。 有关实现的详细信息,请参阅JSON 转换器。​​ObjectToMapTransformer​​​​JsonObjectMapper​​​​JsonObjectMapper​

流变压器

Thetransformspayloads to a( or aif ais 提供)。​​StreamTransformer​​​​InputStream​​​​byte[]​​​​String​​​​charset​

下面的示例演示如何在 XML 中使用元素:​​stream-transformer​

<int:stream-transformer input-channel="directInput" output-channel="output"/> <!-- byte[] -->

<int:stream-transformer id="withCharset" charset="UTF-8"
input-channel="charsetChannel" output-channel="output"/> <!-- String -->

以下示例演示如何使用类和注释在 Java 中配置流转换器:​​StreamTransformer​​​​@Transformer​

@Bean
@Transformer(inputChannel = "stream", outputChannel = "data")
public StreamTransformer streamToBytes() {
return new StreamTransformer(); // transforms to byte[]
}

@Bean
@Transformer(inputChannel = "stream", outputChannel = "data")
public StreamTransformer streamToString() {
return new StreamTransformer("UTF-8"); // transforms to String
}
JSON 转换器

Spring Integration 提供了对象到 JSON 和 JSON 到对象的转换器。 以下一对示例演示如何在 XML 中声明它们:

<int:object-to-json-transformer input-channel="objectMapperInput"/>
<int:json-to-object-transformer input-channel="objectMapperInput"
type="foo.MyDomainObject"/>

默认情况下,前面清单中的转换器使用原版。 它基于类路径中的实现。 您可以使用适当的选项或基于所需的库(例如 GSON)提供自己的自定义实现,如以下示例所示:​​JsonObjectMapper​​​​JsonObjectMapper​

<int:json-to-object-transformer input-channel="objectMapperInput"
type="something.MyDomainObject" object-mapper="customObjectMapper"/>


从版本 3.0 开始,该属性引用新策略接口的实例: 此抽象允许使用 JSON 映射器的多个实现。 提供了包装Jackson 2​的实现,并在类路径上检测到版本。 类分别是。​​object-mapper​​​​JsonObjectMapper​​​​Jackson2JsonObjectMapper​


您可能希望考虑使用aor工厂方法来创建具有所需特征的。 下面的示例演示如何使用此类工厂:​​FactoryBean​​​​JsonObjectMapper​

public class ObjectMapperFactory {

public static Jackson2JsonObjectMapper getMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
return new Jackson2JsonObjectMapper(mapper);
}
}

下面的示例演示如何在 XML 中执行相同的操作

<bean id="customObjectMapper" class="something.ObjectMapperFactory"
factory-method="getMapper"/>


从版本 2.2 开始,默认情况下,如果输入消息还没有该标头,则将标头设置为。​​object-to-json-transformer​​​​content-type​​​​application/json​



如果您希望将标头设置为其他值或用某个值(包括)显式覆盖任何现有标头,请使用属性。 如果要禁止设置标头,请将属性设置为空字符串 ()。 这样做会导致消息带有 noheader,除非输入消息中存在这样的标头。​​content-type​​​​application/json​​​​content-type​​​​content-type​​​​""​​​​content-type​


从版本 3.0 开始,将反映源类型的标头添加到消息中。 同样,在将 JSON 转换为对象时可以使用这些类型标头。 这些标头映射在 AMQP 适配器中,以便它们与 Spring-AMQPJsonMessageConverter 完全兼容。​​ObjectToJsonTransformer​​​​JsonToObjectTransformer​

这使以无需任何特殊配置即可工作:

  • ​…→amqp-outbound-adapter---→​
  • ​---→amqp-inbound-adapter→json-to-object-transformer→…​
    其中出站适配器配置了 a,入站适配器使用默认值。JsonMessageConverterSimpleMessageConverter
  • ​…→object-to-json-transformer→amqp-outbound-adapter---→​
  • ​---→amqp-inbound-adapter→…​
    其中出站适配器配置了 a,入站适配器使用默认值。SimpleMessageConverterJsonMessageConverter
  • ​…→object-to-json-transformer→amqp-outbound-adapter---→​
  • ​---→amqp-inbound-adapter→json-to-object-transformer→​
    其中两个适配器都配置了 a。SimpleMessageConverter

使用标头确定类型时,不应提供 aattribute,因为它优先于标头。​​class​

除了 JSON 转换器之外,Spring Integration 还提供了用于表达式的内置 SpEL 函数。 有关更多信息,请参阅Spring 表达式语言 (SpEL)。​​#jsonPath​

从 3.0 版开始,Spring Integration 还提供了用于表达式的内置 SpEL 函数。 有关详细信息#xpath请参阅SpEL 函数。​​#xpath​

从版本 4.0 开始,支持属性,以指定节点 JSON 表示形式。 结果节点树表示取决于所提供的实现。 默认情况下,theuses aand 将对象对节点树的转换委托给方法。 节点 JSON 表示法提供了使用下游消息流使用 SpEL 表达式访问 JSON 数据属性的效率。 有关详细信息,请参阅属性访问器。​​ObjectToJsonTransformer​​​​resultType​​​​JsonObjectMapper​​​​ObjectToJsonTransformer​​​​Jackson2JsonObjectMapper​​​​ObjectMapper#valueToTree​​​​JsonPropertyAccessor​

从版本 5.1 开始,可以配置为生成带有有效负载的消息,以便在使用此数据类型操作的下游处理程序时方便使用。​​resultType​​​​BYTES​​​​byte[]​

从版本 5.2 开始,可以在使用目标 JSON 处理器反序列化期间配置 ato 支持泛型。 此外,此组件现在首先查询请求消息标头,以查找是否存在 theorand 回退到配置的类型。 Thenow 还根据任何可能的下游方案的请求消息有效负载填充标头。​​JsonToObjectTransformer​​​​ResolvableType​​​​JsonHeaders.RESOLVABLE_TYPE​​​​JsonHeaders.TYPE_ID​​​​ObjectToJsonTransformer​​​​JsonHeaders.RESOLVABLE_TYPE​

从版本 5.2.6 开始,可以提供 ato 解析 a,以便在运行时根据请求消息从 JSON 转换有效负载。 默认情况下,它会在请求消息中进行查询。 如果此表达式返回或构建抛出 a,则转换器将回退到提供的。 此逻辑作为表达式存在,因为可能没有实际的类值,而是一些必须根据某些外部注册表映射到目标类的类型 ID。​​JsonToObjectTransformer​​​​valueTypeExpression​​​​ResolvableType​​​​JsonHeaders​​​​null​​​​ResolvableType​​​​ClassNotFoundException​​​​targetType​​​​JsonHeaders​

阿帕奇阿夫罗变压器

版本 5.2 添加了简单的转换器来转换到 Apache Avro。

它们并不复杂,因为没有架构注册表;转换器只是使用嵌入在从 Avro 模式生成的实现中的架构。​​SpecificRecord​

发送到的消息必须具有实现的有效负载;变压器可以处理多种类型。 必须配置aclass,该类用作反序列化的默认类型。 您还可以指定 SpEL 表达式以确定要使用该方法反序列化的类型。 默认的 SpEL 表达式是 (),默认情况下,它由源类的完全限定类名填充。 如果表达式返回,则使用表达式。​​SimpleToAvroTransformer​​​​SpecificRecord​​​​SimpleFromAvroTransformer​​​​SpecificRecord​​​​setTypeExpression​​​​headers[avro_type]​​​​AvroHeaders.TYPE​​​​SimpleToAvroTransformer​​​​null​​​​defaultType​

也有方法。 这允许分离生产者和使用者,其中发送方可以将标头设置为表示类型的某个令牌,然后使用者将该令牌映射到类型。​​SimpleToAvroTransformer​​​​setTypeExpression​

使用注释配置转换器

您可以向需要类型或消息负载类型的方法添加注释。 返回值的处理方式与前面描述<转换器>元素的部分中描述的方式完全相同。 以下示例演示如何使用注释将 ainto 转换为:​​@Transformer​​​​Message​​​​@Transformer​​​​String​​​​Order​

@Transformer
Order generateOrder(String productId) {
return new Order(productId);
}

转换器方法也可以接受注释,如中所述。 以下示例显示如何使用注释:​​@Header​​​​@Headers​​​​Annotation Support​​​​@Header​

@Transformer
Order generateOrder(String productId, @Header("customerName") String customer) {
return new Order(productId, customer);
}

另请参阅使用注释为端点提供建议。

标头筛选器

有时,转换用例可能就像删除几个标头一样简单。 对于这样的用例,Spring 集成提供了一个标头过滤器,允许您指定应从输出消息中删除的某些标头名称(例如,出于安全原因删除标头或仅暂时需要的值)。 基本上,标头筛选器与标头丰富器相反。 后者在标头丰富器中讨论。 下面的示例定义标头筛选器:

<int:header-filter input-channel="inputChannel"
output-channel="outputChannel" header-names="lastName, state"/>

如您所见,标头过滤器的配置非常简单。 它是具有输入和输出通道以及属性的典型端点。 该属性接受需要删除的标头的名称(如果有多个标头,则用逗号分隔)。 因此,在前面的示例中,名为“lastName”和“state”的标头不出现在出站邮件中。​​header-names​

内容丰富器

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

Spring 集成模块包括两个丰富器:Core

  • 标头扩充器
  • 有效载荷丰富器

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

  • XPath Header Enricher (XML Module)
  • 邮件头丰富器(邮件模块)
  • XMPP 标头扩充器(XMPP 模块)

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

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

标头扩充器

如果只需要向消息添加标头,并且标头不是由消息内容动态确定的,则引用转换器的自定义实现可能有点过分。 出于这个原因,Spring 集成提供了对标头丰富器模式的支持。 它通过元素暴露。 以下示例演示如何使用它:​​<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>

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

<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”的泛型子元素。​​errorChannel​​​​correlationId​​​​priority​​​​replyChannel​​​​routing-slip​​​​<header>​

从版本 4.1 开始,标头扩充器提供了一个子元素。 有关详细信息,请参阅传送名单。​​routing-slip​

POJO支持

通常,标头值不能静态定义,必须根据消息中的某些内容动态确定。 这就是为什么标头扩充器还允许您使用 theandattributes 指定 Bean 引用的原因。 指定的方法计算标头值。 考虑以下配置和具有修改 a 的方法的 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 Expression Language(SpEL)的便利性,以帮助配置许多不同的组件。 标头丰富器就是其中之一。 再看前面显示的 POJO 示例。 您可以看到,确定标头值的计算逻辑非常简单。 一个自然的问题是:“有没有更简单的方法来实现这一点? 这就是SpEL展示其真正力量的地方。 请考虑以下示例:

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

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

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

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

@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 表达式的标头。

使用 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(...);
}
标头通道注册表

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

使用此功能需要存在 abean。 默认情况下,框架创建的默认到期时间(60 秒)。 在此时间之后,通道将从注册表中删除。 若要更改此行为,请使用 anof 定义 Bean,并使用构造函数参数(以毫秒为单位)配置所需的默认延迟。​​HeaderChannelRegistry​​​​DefaultHeaderChannelRegistry​​​​id​​​​integrationHeaderChannelRegistry​

从版本 4.1 开始,您可以设置一个调用 totoon thedefinition 的属性,并且映射条目在首次使用时立即删除。 这在大容量环境中以及通道仅使用一次而不是等待收割者将其删除时可能很有用。​​removeOnGet​​​​true​​​​<bean/>​

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

"@integrationHeaderChannelRegistry.runReaper()"

此子元素非常方便,等效于指定以下配置:

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

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>

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

有效载荷丰富器

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

Spring Integration 2.1引入了有效载荷丰富器。 有效负载扩充器定义一个终结点,该终结点将 a传递给公开的请求通道,然后需要回复消息。 然后,回复消息将成为计算表达式以丰富目标有效负载的根对象。​​Message​

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

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

将消息发送到请求通道时,还可以选择使用 theattribute 仅发送原始有效负载的子集。​​request-payload-expression​

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

如果只需要使用静态值来丰富有效负载,则无需提供属性。​​request-channel​

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

配置

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

<int:enricher request-channel=""                           
auto-startup="true"
id=""
order=""
output-channel=""
request-payload-expression=""
reply-channel=""
error-channel=""
send-timeout=""
should-clone-payload="false">
<int:poller></int:poller>
<int:property name="" expression="" null-result-expression="'Could not determine the name'"/>
<int:property name="" value="23" type="java.lang.Integer" null-result-expression="'0'"/>
<int:header name="" expression="" null-result-expression=""/>
<int:header name="" value="" overwrite="" type="" null-result-expression=""/>
</int:enricher>

将消息发送到以获取用于扩充的数据的通道。 自选。

生命周期属性,指示是否应在应用程序上下文启动期间启动此组件。 默认值为 true。 自选。

底层 Bean 定义的 ID,可以是 anor a。 自选。​​EventDrivenConsumer​​​​PollingConsumer​

指定此终结点作为订阅者连接到通道时的调用顺序。 当该通道使用“故障转移”调度策略时,这一点尤其重要。 当此终结点本身是具有队列的通道的轮询使用者时,它不起作用。 自选。

标识在此终结点处理消息后向其发送消息的消息通道。 自选。

默认情况下,原始消息的有效负载用作发送到的有效负载。 通过将 SpEL 表达式指定为 theattribute 的值,您可以使用原始有效负载的子集、标头值或任何其他可解析的 SpEL 表达式作为发送到请求通道的有效负载的基础。 对于表达式计算,完整的消息可用作“根对象”。 例如,以下 SpEL 表达式(以及其他表达式)是可能的:,,,​​request-channel​​​​request-payload-expression​​​​payload.something​​​​headers.something​​​​new java.util.Date()​​​​'thing1' + 'thing2'​

预期回复消息的通道。 这是可选的。 通常,自动生成的临时回复通道就足够了。 自选。

如果 anis 发生在下游,则向其发送的通道。 这使您能够返回用于扩充的替代对象。 如果未设置,则向调用方抛出 anis。 自选。​​ErrorMessage​​​​Exception​​​​request-channel​​​​Exception​

向通道发送消息时等待的最长时间(以毫秒为单位)(如果通道可能阻塞)。 例如,如果达到队列通道的最大容量,则队列通道可以阻塞,直到空间可用。 在内部,超时设置在 上并最终在调用发送操作时应用。 默认情况下,根据实现情况,无限期阻止。 自选。​​send()​​​​MessagingTemplate​​​​MessageChannel​​​​send() timeout is set to '-1', which can cause the send operation on the `MessageChannel​

布尔值,指示在将消息发送到请求通道以获取扩充数据之前是否应克隆实现的任何有效负载。 克隆的版本将用作最终回复的目标有效载荷。 默认值为。 自选。​​Cloneable​​​​false​

允许您配置消息轮询器(如果此终结点是轮询使用者)。 自选。

每个子元素提供属性的名称(通过强制属性)。 该属性应在目标有效负载实例上设置。 还必须提供恰好一个理论属性 — 前者用于设置文本值,后者用于要计算的 SpEL 表达式。 评估上下文的根对象是从此扩充器启动的流返回的消息 — 如果没有请求通道或应用程序上下文(使用 SpEL 语法),则为输入消息。 从版本 4.0 开始,在指定属性时,还可以指定可选属性。 当目标是类型化 setter 方法时,框架会适当地强制值(只要 a)存在以处理转换。 但是,如果目标有效负载为 a,则使用不进行转换的值填充该条目。 例如,该属性允许您将包含数字转换为目标有效负载中的值。 从版本 4.1 开始,您还可以指定可选属性。 当返回 null 时,将对其进行计算,并改为返回计算的输出。​​property​​​​name​​​​value​​​​expression​​​​@<beanName>.<beanProperty>​​​​value​​​​type​​​​PropertyEditor​​​​Map​​​​type​​​​String​​​​Integer​​​​null-result-expression​​​​enricher​

每个子元素提供消息头的名称(通过强制属性)。 还必须提供恰好一个理论属性 — 前者用于设置文本值,后者用于计算 SpEL 表达式。 评估上下文的根对象是从此扩充器启动的流返回的消息 — 如果没有请求通道或应用程序上下文(使用“@<beanName>.<beanProperty>' SpEL 语法,则为输入消息。 请注意,与元素的元素具有属性类似。 但是,一个关键的区别是,默认情况下,属性与元素的子元素保持一致。 从版本 4.1 开始,您还可以指定可选属性。 当返回 null 时,将对其进行计算,并改为返回计算的输出。​​header​​​​name​​​​value​​​​expression​​​​<header-enricher>​​​​<enricher>​​​​header​​​​type​​​​overwrite​​​​<enricher>​​​​overwrite​​​​true​​​​<enricher>​​​​<property>​​​​null-result-expression​​​​enricher​

例子

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


此处显示的代码示例是 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>

The有几个属性,但只有最初设置的属性。 扩充器的属性配置为传递给。​​User​​​​username​​​​request-channel​​​​User​​​​findUserServiceChannel​

通过隐式设置,返回 aobject,并使用 sub-元素提取回复中的属性并用于丰富原始有效负载。​​reply-channel​​​​User​​​​property​

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

使用 aattribute 时,可以将有效负载的单个属性而不是完整消息传递到请求通道。 在以下示例中,用户名属性将传递到请求通道:​​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​

如何丰富由集合数据组成的有效负载?

在下面的示例中,传入的不是 aobject:​​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>

包含映射键下的用户名。 只有 theis 传递给请求通道。 回复包含一个完整的对象,该对象最终被添加到 thekey 下。​​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>

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

理赔检查

在前面的部分中,我们介绍了几个内容丰富组件,这些组件可以帮助您处理消息缺少一段数据的情况。 我们还讨论了内容筛选,它允许您从消息中删除数据项。 但是,有时我们想暂时隐藏数据。 例如,在分布式系统中,我们可能会收到具有非常大有效负载的消息。 某些间歇性消息处理步骤可能不需要访问此有效负载,而某些步骤可能只需要访问某些标头,因此在每个处理步骤中携带大型消息有效负载可能会导致性能下降,可能产生安全风险,并可能使调试更加困难。

存储在库中(或声明检查)模式描述了一种机制,该机制允许您将数据存储在已知位置,同时仅维护指向该数据所在位置的指针(声明检查)。 您可以将该指针作为新消息的有效负载传递,从而使消息流中的任何组件在需要时立即获取实际数据。 这种方法与挂号信流程非常相似,在挂号信流程中,您在邮箱中收到索赔支票,然后必须去邮局领取您的实际包裹。 这也与航班后或酒店中的行李领取相同。

Spring 集成提供两种类型的索赔检查变压器:

  • 传入索赔检查变压器
  • 传出索赔检查变压器

Convenient namespace-based mechanisms are available to configure them.

Incoming Claim Check Transformer

An incoming claim check transformer transforms an incoming message by storing it in the message store identified by its attribute. The following example defines an incoming claim check transformer:​​message-store​

<int:claim-check-in id="checkin"
input-channel="checkinChannel"
message-store="testMessageStore"
output-channel="output"/>

在前面的配置中,在 上接收的消息将保存到使用属性标识的消息存储中,并使用生成的 ID 进行索引。 该 ID 是该邮件的声明检查。 声明检查也成为发送到的新(转换)消息的有效负载。​​input-channel​​​​message-store​​​​output-channel​

现在,假设在某些时候您确实需要访问实际消息。 您可以手动访问邮件存储并获取邮件的内容,也可以使用相同的方法(创建转换器),只是现在使用传出声明检查转换器将声明检查转换为实际消息。

以下清单概述了传入声明校验转换器的所有可用参数:

<int:claim-check-in auto-startup="true"             
id=""
input-channel=""
message-store="messageStore"
order=""
output-channel=""
send-timeout="">
<int:poller></int:poller>
</int:claim-check-in>

生命周期属性,指示是否应在应用程序上下文启动期间启动此组件。 它默认为。 此属性在元素中不可用。 自选。​​true​​​​Chain​

标识底层 Bean 定义的 ID ()。 此属性在元素中不可用。 自选。​​MessageTransformingHandler​​​​Chain​

此终结点的接收消息通道。 此属性在元素中不可用。 自选。​​Chain​

引用此权利要求检查变压器所使用的内容。 如果未指定,则默认引用为名为 Bean。 自选。​​MessageStore​​​​messageStore​

指定此终结点作为订阅者连接到通道时的调用顺序。 当该通道使用调度策略时,这一点尤其重要。 当此终结点本身是具有队列的通道的轮询使用者时,它不起作用。 此属性在元素中不可用。 自选。​​failover​​​​Chain​

标识消息在此终结点处理后发送到的消息通道。 此属性在元素中不可用。 自选。​​Chain​

指定向输出通道发送回复消息时等待的最长时间(以毫秒为单位)。 默认为 — 无限期阻止。 此属性在元素中不可用。 自选。​​-1​​​​Chain​

定义轮询器。 此元素在元素中不可用。 自选。​​Chain​

传出索赔检查变压器

传出声明检查转换器允许您将具有声明检查有效负载的消息转换为以原始内容作为其有效负载的消息。

<int:claim-check-out id="checkout"
input-channel="checkoutChannel"
message-store="testMessageStore"
output-channel="output"/>

在上述配置中,在 上收到的消息应具有声明检查作为其有效负载。 传出声明检查转换器通过查询消息存储区以获取由提供的声明检查标识的消息,将其转换为具有原始有效负载的消息。 然后,它将新签出的消息发送到。​​input-channel​​​​output-channel​

以下清单概述了传出声明校验转换器的所有可用参数:

<int:claim-check-out auto-startup="true"             
id=""
input-channel=""
message-store="messageStore"
order=""
output-channel=""
remove-message="false"
send-timeout="">
<int:poller></int:poller>
</int:claim-check-out>

生命周期属性,指示是否应在应用程序上下文启动期间启动此组件。 它默认为。 此属性在元素中不可用。 自选。​​true​​​​Chain​

标识底层 Bean 定义的 ID ()。 此属性在元素中不可用。 自选。​​MessageTransformingHandler​​​​Chain​

此终结点的接收消息通道。 此属性在元素中不可用。 自选。​​Chain​

引用此权利要求检查变压器所使用的内容。 如果未指定,则默认引用为名为 Bean。 自选。​​MessageStore​​​​messageStore​

指定此终结点作为订阅者连接到通道时的调用顺序。 当该渠道使用调度策略时,这一点尤其重要。 当此终结点本身是具有队列的通道的轮询使用者时,它不起作用。 此属性在元素中不可用。 自选。​​failover​​​​Chain​

标识消息在此终结点处理后发送到的消息通道。 此属性在元素中不可用。 自选。​​Chain​

如果设置为 ,则此转换器将从中删除消息。 当邮件只能“声明”一次时,此设置很有用。 它默认为。 自选。​​true​​​​MessageStore​​​​false​

指定向输出通道发送回复消息时等待的最长时间(以毫秒为单位)。 它默认为 — 无限期阻止。 此属性在元素中不可用。 自选。​​-1​​​​Chain​

定义轮询器。 此元素在元素中不可用。 自选。​​Chain​

一次索赔

有时,特定邮件只需声明一次。 作为类比,考虑处理飞机行李的过程。 您在出发时托运行李,并在抵达时领取行李。 行李一旦被认领,如果不先重新托运,就不能再次领取。 为了适应这种情况,我们在变压器上引入了 aboolean 属性。 此属性默认设置为。 但是,如果设置为 ,则从 中移除已声明的邮件,以便无法再次声明该消息。​​remove-message​​​​claim-check-out​​​​false​​​​true​​​​MessageStore​

此功能在存储空间方面有影响,尤其是在基于内存的情况下,未能删除消息最终可能导致 因此,如果您不希望进行多个声明,我们建议您将属性的值设置为 。 以下示例演示如何使用属性:​​Map​​​​SimpleMessageStore​​​​OutOfMemoryException​​​​remove-message​​​​true​​​​remove-message​

<int:claim-check-out id="checkout"
input-channel="checkoutChannel"
message-store="testMessageStore"
output-channel="output"
remove-message="true"/>

消息存储库中的一句话

尽管我们很少关心声明检查的细节(只要它们有效),但您应该知道,Spring 集成中实际声明检查(指针)的当前实现使用 UUID 来确保唯一性。

​org.springframework.integration.store.MessageStore​​是用于存储和检索消息的策略接口。 Spring 集成提供了两个方便的实现:

  • ​SimpleMessageStore​​:基于内存中的实现(默认,适合测试)Map
  • ​JdbcMessageStore​​:通过 JDBC 使用关系数据库的实现

编 解码 器

Spring Integration 的 4.2 版引入了抽象。 编解码器对对象进行编码和解码。 它们提供了 Java 序列化的替代方案。 一个优点是,通常,对象不需要实现。 我们提供了一个使用Kryo进行序列化的实现,但您可以提供自己的实现以用于以下任何组件:​​Codec​​​​byte[]​​​​Serializable​

  • ​EncodingPayloadTransformer​
  • ​DecodingTransformer​
  • ​CodecMessageConverter​

​EncodingPayloadTransformer​

此转换器使用编解码器将有效负载编码为 a。 它不会影响邮件头。​​byte[]​

有关更多信息,请参阅Javadoc。

​DecodingTransformer​

此转换器使用编解码器进行解码。 它需要配置对象应该解码到的对象(或解析为 a 的表达式)。 如果生成的对象为 a,则不保留入站标头。​​byte[]​​​​Class​​​​Class​​​​Message<?>​

有关更多信息,请参阅Javadoc。

​CodecMessageConverter​

某些端点(如 TCP 和 Redis)没有消息头的概念。 它们支持使用 a,并且可用于将消息转换为 afor 传输或从 afor 传输。​​MessageConverter​​​​CodecMessageConverter​​​​byte[]​

有关更多信息,请参阅Javadoc。

克里奥

目前,这是唯一的实现,它提供了两种:​​Codec​​​​Codec​

  • ​PojoCodec​​:用于变压器
  • ​MessageCodec​​:用于CodecMessageConverter

该框架提供了几个自定义序列化程序:

  • ​FileSerializer​
  • ​MessageHeadersSerializer​
  • ​MutableMessageHeadersSerializer​

第一个可以与 通过初始化它。 第二个和第三个与 一起使用,其初始化为 。​​PojoCodec​​​​FileKryoRegistrar​​​​MessageCodec​​​​MessageKryoRegistrar​

定制克里奥

默认情况下,Kryo 将未知的 Java 类型委托给它。 Kryo 还为每个基元类型注册默认序列化程序,以及 ,并且 .use 反射来导航对象图。 更有效的方法是实现自定义序列化程序,该序列化程序知道对象的结构,并可以直接序列化选定的基元字段。 下面的示例演示了这样的序列化程序:​​FieldSerializer​​​​String​​​​Collection​​​​Map​​​​FieldSerializer​

public class AddressSerializer extends Serializer<Address> {

@Override
public void write(Kryo kryo, Output output, Address address) {
output.writeString(address.getStreet());
output.writeString(address.getCity());
output.writeString(address.getCountry());
}

@Override
public Address read(Kryo kryo, Input input, Class<Address> type) {
return new Address(input.readString(), input.readString(), input.readString());
}
}

接口公开 和 ,提供对包含哪些字段和其他内部设置的完全控制,如Kryo 文档中所述。​​Serializer​​​​Kryo​​​​Input​​​​Output​

注册自定义序列化程序时,需要一个注册 ID。 注册 ID 是任意的。 但是,在我们的例子中,必须显式定义 ID,因为分布式应用程序中的每个 Kryo 实例都必须使用相同的 ID。 Kryo 建议使用小的正整数,并保留一些 id(值< 10)。 Spring 集成目前默认使用 40、41 和 42(对于前面提到的文件和消息头序列化程序)。 我们建议您从 60 开始,以便在框架中进行扩展。 您可以通过配置前面提到的注册器来覆盖这些框架默认值。

使用自定义 Kryo 序列化程序

如果您需要自定义序列化,请参阅Kryo文档,因为您需要使用本机 API 进行自定义。 有关示例,请参阅消息编解码器实现。

实现 KryoSerializable

如果您有权访问域对象源代码,则可以实现此处所述。 在这种情况下,类提供序列化方法本身,不需要进一步的配置。 但是,基准测试表明,这不如显式注册自定义序列化程序有效。 以下示例显示了一个自定义 Kryo 序列化程序:​​write​​​​KryoSerializable​

public class Address implements KryoSerializable {
...

@Override
public void write(Kryo kryo, Output output) {
output.writeString(this.street);
output.writeString(this.city);
output.writeString(this.country);
}

@Override
public void read(Kryo kryo, Input input) {
this.street = input.readString();
this.city = input.readString();
this.country = input.readString();
}
}

您还可以使用此技术包装 Kryo 以外的序列化库。

使用注释​​@DefaultSerializer​

Kryo 还提供了注释,如此处所述。​​@DefaultSerializer​

@DefaultSerializer(SomeClassSerializer.class)
public class SomeClass {
// ...
}

如果您有权访问域对象,这可能是指定自定义序列化程序的更简单方法。 请注意,这不会使用 ID 注册类,这可能会使该技术在某些情况下没有帮助。​​write​