Transformer

消息转换器在启用消息生成者和消息使用者的松散耦合方面发挥着非常重要的作用。你可以添加那些组件之间的转换器,而不是要求每个消息生成组件知道下一个使用程序期望的类型。通用的转换器(例如将 String 转换为 XML 文档的转换器)也高度可重复使用。

Message transformers play a very important role in enabling the loose-coupling of message producers and message consumers. Rather than requiring every message-producing component to know what type is expected by the next consumer, you can add transformers between those components. Generic transformers, such as one that converts a String to an XML Document, are also highly reusable.

对于某些系统,最好提供 canonical data model,但 Spring 集成的总体理念是不需要任何特定格式。相反,为了实现最大的灵活性,Spring 集成旨在为扩展提供尽可能简单的模型。与其他端点类型一样,在 XML 或 Java 注释中使用声明性配置,可以将简单的 POJO 调整为消息转换器的角色。本章的其余部分介绍这些配置选项。

For some systems, it may be best to provide a canonical data model, but Spring Integration’s general philosophy is not to require any particular format. Rather, for maximum flexibility, Spring Integration aims to provide the simplest possible model for extension. As with the other endpoint types, the use of declarative configuration in XML or Java annotations enables simple POJOs to be adapted for the role of message transformers. The rest of this chapter describes these configuration options.

为了最大限度地提高灵活性,Spring 不要求基于 XML 的消息负载。但是,如果这确实是您应用程序的正确选择,则该框架确实提供了一些方便的转换器来处理基于 XML 的负载。有关这些转换器的更多信息,请参阅 XML Support - Dealing with XML Payloads

For the sake of maximizing flexibility, Spring does not require XML-based message payloads. Nevertheless, the framework does provide some convenient transformers for dealing with XML-based payloads if that is indeed the right choice for your application. For more information on those transformers, see XML Support - Dealing with XML Payloads.

Configuring a Transformer with Java and other DSLs

对于简单的 Java 和注释配置,Spring Bean POJO 方法必须用 @Transformer 注释标记,并且在从输入通道使用消息时,该框架会调用它:

For simple Java & Annotation configuration, the Spring bean POJO method must be marked with a @Transformer annotation and the framework calls it when messages are consumed from an input channel:

public class SomeService {

    @Transformer(inputChannel = "transformChannel", outputChannel = "nextServiceChannel")
    public OutputData exampleTransformer(InputData payload) {
        ...
    }

}

Annotation Support 中查看更多信息。

See more information in the Annotation Support.

对于 Java、Groovy 或 Kotlin DSL,IntegrationFlow.transform() 运算符表示转换器端点:

For Java, Groovy or Kotlin DSLs, the .transform() operator of an IntegrationFlow represents a transformer endpoint:

  • Java DSL

  • Kotlin DSL

  • Groovy DSL

@Bean
public IntegrationFlow someFlow() {
    return IntegrationFlow
             .from("transformChannel")
             .transform(someService, "exampleTransformer")
             .channel("nextServiceChannel")
             .get();
}
@Bean
fun someFlow() =
    integrationFlow("transformChannel") {
        transform(someService, "exampleTransformer")
        channel("nextServiceChannel")
    }
@Bean
someFlow() {
    integrationFlow 'transformChannel',
            {
                transform someService, 'exampleTransformer'
                channel 'nextServiceChannel'
            }
}

请参阅各个章节中有关 DSL 的更多信息:

See more information about the DSLs in the respective chapters:

Configuring a Transformer with XML

<transformer> 元素用于创建消息转换端点。除了 input-channeloutput-channel 属性外,它还要求提供 ref 属性。ref 可以指向一个对象,该对象在一个方法上包含 @Transformer 注释(参见 Configuring a Transformer with Annotations),或者可以与 method 属性中提供的显式方法名称值结合使用。

The <transformer> element is used to create a message-transforming endpoint. In addition to input-channel and output-channel attributes, it requires a ref attribute. The ref may either point to an object that contains the @Transformer annotation on a single method (see Configuring a Transformer with Annotations), or it may be combined with an explicit method name value provided in the method attribute.

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

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

Using a ref attribute is generally recommended if the custom transformer handler implementation can be reused in other <transformer> definitions. However, if the custom transformer handler implementation should be scoped to a single definition of the <transformer>, you can define an inner bean definition, as the following example shows:

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

在同一个 <transformer> 配置文件中同时使用 ref 属性和内部处理程序定义是不允许的,因为这会造成歧义情形,并导致抛出异常。

Using both the ref attribute and an inner handler definition in the same <transformer> configuration is not allowed, as it creates an ambiguous condition and results in an exception being thrown.

如果 ref 属性引用扩展 AbstractMessageProducingHandler 的 Bean(例如框架本身提供的转换器),可以通过将输出信道直接注入处理程序来对配置进行优化。在这种情况下,每个 ref 都必须指向一个单独的 Bean 实例(或 prototype 作用域的 Bean),或者使用内部 <bean/> 配置类型。如果您无意中从多个 Bean 引用相同的邮件处理程序,您将收到配置异常。

If the ref attribute references a bean that extends AbstractMessageProducingHandler (such as transformers provided by the framework itself), the configuration is optimized by injecting the output channel into the handler directly. In this case, each ref must be to a separate bean instance (or a prototype-scoped bean) or use the inner <bean/> configuration type. If you inadvertently reference the same message handler from multiple beans, you get a configuration exception.

当使用 POJO 时,用于转换的方法可能需要 Message 类型或入站消息的有效负载类型。它还可以通过分别使用 @Header@Headers 参数注解,以单个或完整映射的方式接受消息标题值。该方法的返回值可以是任何类型。如果返回值本身是 Message,则会传递给转换器的输出通道。

When using a POJO, the method that is used for transformation may expect either the Message type or the payload type of inbound messages. It may also accept message header values either individually or as a full map by using the @Header and @Headers parameter annotations, respectively. The return value of the method can be any type. If the return value is itself a Message, that is passed along to the transformer’s output channel.

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

As of Spring Integration 2.0, a message transformer’s transformation method can no longer return null. Returning null results in an exception, because a message transformer should always be expected to transform each source message into a valid target message. In other words, a message transformer should not be used as a message filter, because there is a dedicated <filter> option for that. However, if you do need this type of behavior (where a component might return null and that should not be considered an error), you could use a service activator. Its requires-reply value is false by default, but that can be set to true in order to have exceptions thrown for null return values, as with the transformer.

Transformers and Spring Expression Language (SpEL)

与路由器、聚合器和其他组件一样,从 Spring 集成 2.0 开始,当转换逻辑相对简单时,转换器也可以受益于 SpEL support。以下示例展示了如何使用 SpEL 表达式:

Like routers, aggregators, and other components, as of Spring Integration 2.0, transformers can also benefit from SpEL support whenever transformation logic is relatively simple. The following example shows how to use a SpEL expression:

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

前一个示例在不编写自定义转换器的情况下转换有效负载。我们的有效负载(假设是一个 String)被大写化、与当前时间戳串联并应用了一些格式。

The preceding example transforms the payload without writing a custom transformer. Our payload (assumed to be a String) is upper-cased, concatenated with the current timestamp, and has some formatting applied.

Common Transformers

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

Spring Integration provides a few transformer implementations.

Object-to-String Transformer

因为使用 ObjecttoString() 表示相当常见,所以 Spring Integration 提供了 ObjectToStringTransformer(也请参阅 Transformers 工厂),其中输出是带有字符串 payloadMessage。该 String 是对入站消息有效负载调用 toString() 操作的结果。以下示例演示如何声明 object-to-string 转换器的实例:

Because it is fairly common to use the toString() representation of an Object, Spring Integration provides an ObjectToStringTransformer (see also the Transformers factory) where the output is a Message with a String payload. That String is the result of invoking the toString() operation on the inbound Message’s payload. The following example shows how to declare an instance of the object-to-string transformer:

  • Java DSL

  • Kotlin DSL

  • Groovy DSL

  • XML

@Bean
public IntegrationFlow someFlow() {
    return IntegrationFlow
             .from("in")
             .transform(Transformers.objectToString())
             .channel("out")
             .get();
}
@Bean
fun someFlow() =
    integrationFlow("in") {
        transform(Transformers.objectToString())
        channel("out")
    }
@Bean
someFlow() {
    integrationFlow 'in',
            {
                transform Transformers.objectToString()
                channel 'out'
            }
}
<int:object-to-string-transformer input-channel="in" output-channel="out"/>

此转换器的潜在用途是将一些任意对象发送到 file 命名空间中的 'outbound-channel-adapter'。而该通道适配器在默认情况下仅支持 String、字节数组或 java.io.File 有效负载,在适配器处理必要的转换之前,在此适配器之前添加此转换器。只要 toString() 调用的结果是你想要写入文件的内容,这工作得很好。否则,可以使用前面显示的通用 'transformer' 元素提供一个基于 POJO 的自定义转换器。

A potential use for this transformer would be sending some arbitrary object to the 'outbound-channel-adapter' in the file namespace. Whereas that channel adapter only supports String, byte-array, or java.io.File payloads by default, adding this transformer immediately before the adapter handles the necessary conversion. That works fine as long as the result of the toString() call is what you want to be written to the file. Otherwise, you can provide a custom POJO-based transformer by using the generic 'transformer' element shown previously.

在调试时,此转换器通常不是必需的,因为 logging-channel-adapter 能够记录消息负载。有关更多详情,请参阅 Wire Tap

When debugging, this transformer is not typically necessary, since the logging-channel-adapter is capable of logging the message payload. See Wire Tap for more detail.

object-to-string 转换器非常简单。它对入站有效负载调用 toString()。从 Spring Integration 3.0 开始,此规则有两个例外:

The object-to-string transformer is very simple. It invokes toString() on the inbound payload. Since Spring Integration 3.0, there are two exceptions to this rule:

  • If the payload is a char[], it invokes new String(payload).

  • If the payload is a byte[], it invokes new String(payload, charset), where charset is UTF-8 by default. The charset can be modified by supplying the charset attribute on the transformer.

对于更高级的功能(例如在运行时动态选择字符集),您可以改为使用基于 SpEL 表达式的转换器,如下例所示:

For more sophistication (such as selection of the charset dynamically, at runtime), you can use a SpEL expression-based transformer instead, as the following example shows:

  • Java DSL

  • XML

@Bean
public IntegrationFlow someFlow() {
    return IntegrationFlow
             .from("in")
             .transform("new String(payload, headers['myCharset']")
             .channel("out")
             .get();
}
<int:transformer input-channel="in" output-channel="out"
       expression="new String(payload, headers['myCharset']" />

如果你需要将 Object 序列化为字节数组,或将字节数组反序列化回 Object,Spring Integration 提供了对称的序列化转换器。它们默认使用标准 Java 序列化,但你可以使用 serializerdeserializer 属性分别提供 Spring SerializerDeserializer 策略的实现。另请参阅 Transformers 工厂类。以下示例演示如何使用 Spring 的序列化器和反序列化器:

If you need to serialize an Object to a byte array or deserialize a byte array back into an Object, Spring Integration provides symmetrical serialization transformers. These use standard Java serialization by default, but you can provide an implementation of Spring Serializer or Deserializer strategies by using the serializer and deserializer attributes, respectively. See also the Transformers factory class. The following example shows to use Spring’s serializer and deserializer:

  • Java DSL

  • XML

@Bean
public IntegrationFlow someFlow() {
    return IntegrationFlow
             .from("objectsIn")
             .transform(Transformers.serializer())
             .channel("bytesOut")
             .channel("bytesIn")
             .transform(Transformers.deserializer("com.mycom.*", "com.yourcom.*"))
             .channel("objectsOut")
             .get();
}
<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.*"/>

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

When deserializing data from untrusted sources, you should consider adding a allow-list of package and class patterns. By default, all classes are deserialized.

Object-to-Map and Map-to-Object Transformers

Spring Integration 还提供了 ObjectMapMapObject 转换器,这些转换器使用 JSON 来序列化和反序列化对象图。该对象层次结构会内省到最原始类型(Stringint 等)。此类型的路径用 SpEL 描述,这将成为转换后的 Map 中的 key。原始类型将成为值。

Spring Integration also provides Object-to-Map and Map-to-Object transformers, which use the JSON to serialize and de-serialize the object graphs. The object hierarchy is introspected to the most primitive types (String, int, and so on). The path to this type is described with SpEL, which becomes the key in the transformed Map. The primitive type becomes the value.

请考虑以下示例:

Consider the following example:

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

The two classes in the preceding example are transformed to the following Map:

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

基于 JSON 的 Map 让你能够描述对象结构,而无需共享实际类型,只要你保持结构,这就会让你能够将对象图还原和重建到一个类型不同的对象图中。

The JSON-based Map lets you describe the object structure without sharing the actual types, which lets you restore and rebuild the object graph into a differently typed object graph, as long as you maintain the structure.

例如,可以通过使用 MapObject 转换器将前一个结构还原到以下对象图:

For example, the preceding structure could be restored back to the following object graph by using the Map-to-Object transformer:

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
}

如果你需要创建一个"`结构`"映射,你可以提供 flatten 属性。默认值为 'true'。如果将其设置为 'false',该结构就是 Map 类型的 Map 对象。

If you need to create a “structured” map, you can provide the flatten attribute. The default is 'true'. If you set it to 'false', the structure is a Map of Map objects.

请考虑以下示例:

Consider the following example:

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

The two classes in the preceding example are transformed to the following Map:

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

为了配置这些转换器,Spring Integration 提供了相应的 XML 组件和 Java DSL 工厂:

To configure these transformers, Spring Integration provides respective XML component and Java DSL factory:

  • Java DSL

  • XML

@Bean
public IntegrationFlow someFlow() {
    return IntegrationFlow
             .from("directInput")
             .transform(Transformers.toMap())
             .channel("output")
             .get();
}
<int:object-to-map-transformer input-channel="directInput" output-channel="output"/>

此外,你可以将 flatten 特性设置为 false,如下所示:

You can also set the flatten attribute to false, as follows:

  • Java DSL

  • XML

@Bean
public IntegrationFlow someFlow() {
    return IntegrationFlow
             .from("directInput")
             .transform(Transformers.toMap(false))
             .channel("output")
             .get();
}
<int:object-to-map-transformer input-channel="directInput" output-channel="output" flatten="false"/>

Spring Integration 为 Map-to-Object 提供了 XML 命名空间支持,并且 Java DSL 工厂具有 fromMap() 方法,如下例所示:

Spring Integration provides XML namespace support for Map-to-Object and the Java DSL factory has the fromMap() method, as the following example shows:

  • Java DSL

  • XML

@Bean
public IntegrationFlow someFlow() {
    return IntegrationFlow
             .from("input")
             .transform(Transformers.fromMap(org.something.Person.class))
             .channel("output")
             .get();
}
<int:map-to-object-transformer input-channel="input"
                         output-channel="output"
                         type="org.something.Person"/>

或者,你可以使用 ref 特性和原型作用域 bean,如下例所示:

Alternatively, you could use a ref attribute and a prototype-scoped bean, as the following example shows:

  • Java DSL

  • XML

@Bean
IntegrationFlow someFlow() {
    return IntegrationFlow
             .from("inputA")
             .transform(Transformers.fromMap("person"))
             .channel("outputA")
             .get();
}

@Bean
@Scope("prototype")
Person person() {
    return new Person();
}
<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”属性,则必须指向一个“prototype”作用域的 Bean。否则,将抛出一个 BeanCreationException

The 'ref' and 'type' attributes are mutually exclusive. Also, if you use the 'ref' attribute, you must point to a 'prototype' scoped bean. Otherwise, a BeanCreationException is thrown.

从 5.0 版开始,您可以使用自定义 JsonObjectMapper`提供 `ObjectToMapTransformer——当您需要为日期或空集合的空值(以及其他用途)设置特殊格式时。有关 `JsonObjectMapper`实现的更多信息,请参阅 JSON Transformers

Starting with version 5.0, you can supply the ObjectToMapTransformer with a customized JsonObjectMapper — for when you need special formats for dates or nulls for empty collections (and other uses). See JSON Transformers for more information about JsonObjectMapper implementations.

Stream Transformer

StreamTransformerInputStream 有效负载转换为 byte[](或 String,如果提供了 charset)。

The StreamTransformer transforms InputStream payloads to a byte[]( or a String if a charset is provided).

以下示例展示了如何在 XML 中使用 stream-transformer 元素:

The following example shows how to use the stream-transformer element in XML:

  • Java DSL

  • XML

@Bean
public IntegrationFlow someFlow() {
    return IntegrationFlow
             .from("input")
             .transform(Transformers.fromStream("UTF-8"))
             .channel("output")
             .get();
}
<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 注解配置流转换器:

The following example shows how to use the StreamTransformer class and the @Transformer annotation to configure a stream transformer in Java:

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

Spring Integration 提供了 Object-to-JSON 和 JSON-to-Object 转换器。以下示例展示了如何在 XML 中声明它们:

Spring Integration provides Object-to-JSON and JSON-to-Object transformers. The following pair of examples show how to declare them in XML:

<int:object-to-json-transformer input-channel="objectMapperInput"/>

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

默认情况下,上述列表中的转换器使用原生的 JsonObjectMapper。它基于类路径中的实现。你可以提供自己的定制 JsonObjectMapper 实现(带有适当选项或基于必需库(例如 GSON)),如下例所示:

By default, the transformers in the preceding listing use a vanilla JsonObjectMapper. It is based on an implementation from the classpath. You can provide your own custom JsonObjectMapper implementation with appropriate options or based on a required library (such as GSON), as the following example shows:

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

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

Beginning with version 3.0, the object-mapper attribute references an instance of a new strategy interface: JsonObjectMapper. This abstraction lets multiple implementations of JSON mappers be used. Implementation that wraps Jackson 2 is provided, with the version being detected on the classpath. The class is Jackson2JsonObjectMapper, respectively.

你可能想要考虑使用 FactoryBean 或工厂方法使用所需的特性创建 JsonObjectMapper。以下示例展示了如何使用这样的工厂:

You may wish to consider using a FactoryBean or a factory method to create the JsonObjectMapper with the required characteristics. The following example shows how to use such a factory:

public class ObjectMapperFactory {

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

以下示例展示了如何在 XML 中执行相同操作:

The following example shows how to do the same thing in XML:

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

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

Beginning with version 2.2, the object-to-json-transformer sets the content-type header to application/json, by default, if the input message does not already have that header.

如果你想要将 content-type 头设置为其他值,或明确地用某个值(包括 application/json)覆盖任何现有的头,请使用 content-type 特性。如果你想要取消头设置,请将 content-type 特性设置为一个空字符串("")。这样做会创建一个没有 content-type 头的消息,除非输入消息有该头。

If you wish to set the content-type header to some other value or explicitly overwrite any existing header with some value (including application/json), use the content-type attribute. If you wish to suppress the setting of the header, set the content-type attribute to an empty string (""). Doing so results in a message with no content-type header, unless such a header was present on the input message.

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

Beginning with version 3.0, the ObjectToJsonTransformer adds headers, reflecting the source type, to the message. Similarly, the JsonToObjectTransformer can use those type headers when converting the JSON to an object. These headers are mapped in the AMQP adapters so that they are entirely compatible with the Spring-AMQP JsonMessageConverter.

这使得以下流无需任何特殊配置就能工作:

This enables the following flows to work without any special configuration:

  • …​→amqp-outbound-adapter---→

  • ---→amqp-inbound-adapter→json-to-object-transformer→…​[.iokays-translated-bab40cf0bd4bdd59368d1f1fa4810fe7] 其中出站适配器使用 JsonMessageConverter 配置,入站适配器使用默认的 SimpleMessageConverter

Where the outbound adapter is configured with a JsonMessageConverter and the inbound adapter uses the default SimpleMessageConverter. * …​→object-to-json-transformer→amqp-outbound-adapter---→ * ---→amqp-inbound-adapter→…​[.iokays-translated-7d2fcc6883049219ac4f33eb9eb833f1] 其中出站适配器使用 SimpleMessageConverter 配置,入站适配器使用默认的 JsonMessageConverter

Where the outbound adapter is configured with a SimpleMessageConverter and the inbound adapter uses the default JsonMessageConverter. * …​→object-to-json-transformer→amqp-outbound-adapter---→ * ---→amqp-inbound-adapter→json-to-object-transformer→[.iokays-translated-a5a948c2b32651037ccd99bfa984d2d2] 其中两个适配器都使用 SimpleMessageConverter 配置。

Where both adapters are configured with a SimpleMessageConverter.

在使用头来确定类型时,您不应提供 class 属性,因为它优先于头。

When using the headers to determine the type, you should not provide a class attribute, because it takes precedence over the headers.

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

In addition to JSON Transformers, Spring Integration provides a built-in #jsonPath SpEL function for use in expressions. For more information see Spring Expression Language (SpEL).

自 3.0 版本以来,Spring Integration 为表达式使用内置了 #xpath SpEL 函数。有关更多信息,请参阅 #xpath SpEL Function

Since version 3.0, Spring Integration also provides a built-in #xpath SpEL function for use in expressions. For more information see #xpath SpEL Function.

从 4.0 版开始,ObjectToJsonTransformer`支持 `resultType`属性,以指定节点 JSON 表示。结果节点树表示取决于提供的 `JsonObjectMapper`的实现。默认情况下,`ObjectToJsonTransformer`使用 `Jackson2JsonObjectMapper,并将对象转换为节点树的任务委派给 `ObjectMapper#valueToTree`方法。当下游消息流使用能够访问 JSON 数据属性的 SpEL 表达式时,节点 JSON 表示提供了便于使用 `JsonPropertyAccessor`的效率。有关更多信息,请参阅 Property Accessors

Beginning with version 4.0, the ObjectToJsonTransformer supports the resultType property, to specify the node JSON representation. The result node tree representation depends on the implementation of the provided JsonObjectMapper. By default, the ObjectToJsonTransformer uses a Jackson2JsonObjectMapper and delegates the conversion of the object to the node tree to the ObjectMapper#valueToTree method. The node JSON representation provides efficiency for using the JsonPropertyAccessor when the downstream message flow uses SpEL expressions with access to the properties of the JSON data. See Property Accessors for more information.

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

Beginning with version 5.1, the resultType can be configured as BYTES to produce a message with the byte[] payload for convenience when working with downstream handlers which operate with this data type.

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

Starting with version 5.2, the JsonToObjectTransformer can be configured with a ResolvableType to support generics during deserialization with the target JSON processor. Also, this component now consults request message headers first for the presence of the JsonHeaders.RESOLVABLE_TYPE or JsonHeaders.TYPE_ID and falls back to the configured type otherwise. The ObjectToJsonTransformer now also populates a JsonHeaders.RESOLVABLE_TYPE header based on the request message payload for any possible downstream scenarios.

从 5.2.6 版开始,JsonToObjectTransformer 可以提供 valueTypeExpression,以解决请求消息中要从 JSON 转换的有效负载的 ResolvableType。默认情况下,它会在请求消息中查询 JsonHeaders。如果此表达式返回 nullResolvableType 构建抛出 ClassNotFoundException,则转换器会回退到提供的 targetType。此逻辑以表达式的形式存在,因为 JsonHeaders 可能是没有实际类值的类型 ID,这些类型 ID 必须映射到目标类,具体视某些外部注册表而定。

Starting with version 5.2.6, the JsonToObjectTransformer can be supplied with a valueTypeExpression to resolve a ResolvableType for the payload to convert from JSON at runtime against the request message. By default, it consults JsonHeaders in the request message. If this expression returns null or ResolvableType building throws a ClassNotFoundException, the transformer falls back to the provided targetType. This logic is present as an expression because JsonHeaders may not have real class values, but rather some type ids which have to be mapped to target classes according some external registry.

Apache Avro Transformers

5.2 版添加了用于转换到/从 Apache Avro 的简单转换器。

Version 5.2 added simple transformers to transform to/from Apache Avro.

这些转换器比较简单,因为没有模式注册表;转换器仅仅使用从 Avro 模式生成的 SpecificRecord 实现中嵌入的模式。

They are unsophisticated in that there is no schema registry; the transformers simply use the schema embedded in the SpecificRecord implementation generated from the Avro schema.

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

Messages sent to the SimpleToAvroTransformer must have a payload that implements SpecificRecord; the transformer can handle multiple types. The SimpleFromAvroTransformer must be configured with a SpecificRecord class which is used as the default type to deserialize. You can also specify a SpEL expression to determine the type to deserialize using the setTypeExpression method. The default SpEL expression is headers[avro_type] (AvroHeaders.TYPE) which, by default, is populated by the SimpleToAvroTransformer with the fully qualified class name of the source class. If the expression returns null, the defaultType is used.

SimpleToAvroTransformer 还具有 setTypeExpression 方法。这允许解除生产者和消费者的耦合,其中发送方可以将头设置为表示类型的某个令牌,然后使用者再将该令牌映射到类型。

The SimpleToAvroTransformer also has a setTypeExpression method. This allows decoupling of the producer and consumer where the sender can set the header to some token representing the type and the consumer then maps that token to a type.

Protocol Buffers Transformers

6.1 版本增加了对转换 Protocol Buffers 数据内容的支持。

Version 6.1 adds support for transforming from and to Protocol Buffers data content.

ToProtobufTransformercom.google.protobuf.Message 消息有效负载转换为原生字节数组或 JSON 文本有效负载。application/x-protobuf 内容类型(默认使用)生成字节数组输出有效负载。如果内容类型是 application/json,并在类路径中找到 com.google.protobuf:protobuf-java-util,则输出是文本 JSON 有效负载。如果未设置内容类型头,ToProtobufTransformer 则默认为 application/x-protobuf

The ToProtobufTransformer transforms a com.google.protobuf.Message message payloads into native byte array or json text payloads. The application/x-protobuf content type (used by default) produces byte array output payload. If the content type is application/json add the com.google.protobuf:protobuf-java-util if found on the classpath, then the output is text json payload. If the content type header is not set the ToProtobufTransformer defaults to application/x-protobuf.

FromProtobufTransformer 将字节数组或文本 protobuf 有效负载(取决于内容类型)转换回 com.google.protobuf.Message 实例。FromProtobufTransformer 应明确指定一个预期类类型(使用 setExpectedType 方法),或使用 SpEL 表达式以确定使用 setExpectedTypeExpression 方法进行反序列化的类型。默认 SpEL 表达式为 headers[proto_type] (ProtoHeaders.TYPE),由 ToProtobufTransformer 使用源 com.google.protobuf.Message 类的完全限定类名填充。

The FromProtobufTransformer transforms byte array or text protobuf payload (depending on the content type) back into com.google.protobuf.Message instances. The FromProtobufTransformer should specify either an expected class type explicitly (use the setExpectedType method) or use a SpEL expression to determine the type to deserialize using the setExpectedTypeExpression method. The default SpEL expression is headers[proto_type] (ProtoHeaders.TYPE) which is populated by the ToProtobufTransformer with the fully qualified class name of the source com.google.protobuf.Message class.

例如,编译以下 IDL:

For example, compiling the following IDL:

syntax = "proto2";
package tutorial;

option java_multiple_files = true;
option java_package = "org.example";
option java_outer_classname = "MyProtos";

message MyMessageClass {
  optional string foo = 1;
  optional string bar = 2;
}

将生成一个新的 org.example.MyMessageClass 类。

will generate a new org.example.MyMessageClass class.

然后使用:

Then use the:

// Transforms a MyMessageClass instance into a byte array.
ToProtobufTransformer toTransformer = new ToProtobufTransformer();

MyMessageClass test = MyMessageClass.newBuilder()
                                .setFoo("foo")
                                .setBar("bar")
                                .build();
// message1 payload is byte array protocol buffer wire format.
Message message1 = toTransformer.transform(new GenericMessage<>(test));

// Transforms a byte array payload into a MyMessageClass instance.
FromProtobufTransformer fromTransformer = new FromProtobufTransformer();

// message2 payload == test
Message message2 =  fromTransformer.transform(message1);

Configuring a Transformer with Annotations

可以将 @Transformer 注解添加到需要 Message 类型或消息有效负载类型的。返回值的处理方式与前面 [在描述 <transformer> 元素的章节中transformer-namespace] 中描述的一模一样。以下示例显示如何使用 @Transformer 注解将 String 转换成 Order

You can add the @Transformer annotation to methods that expect either the Message type or the message payload type. The return value is handled in the exact same way as described earlier transformer-namespace. The following example shows how to use the @Transformer annotation to transform a String into an Order:

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

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

Transformer methods can also accept the @Header and @Headers annotations, as documented in Annotation Support. The following examples shows how to use the @Header annotation:

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

Header Filter

有时候,您的转换用例可能仅仅是删除一些标头。对于这类用例,Spring Integration 提供了一个标头过滤器,允许您指定应从输出消息中删除的某些标头名称(例如,出于安全原因删除标头或仅暂时需要的值)。基本上,标头过滤器与标头丰富器相反。后者将在 Header Enricher中讨论。以下示例定义了一个标头过滤器:

Sometimes, your transformation use case might be as simple as removing a few headers. For such a use case, Spring Integration provides a header filter that lets you specify certain header names that should be removed from the output message (for example, removing headers for security reasons or a value that was needed only temporarily). Basically, the header filter is the opposite of the header enricher. The latter is discussed in Header Enricher. The following example defines a header filter:

  • Java DSL

  • XML

@Bean
public IntegrationFlow someFlow() {
    return IntegrationFlow
             .from("inputChannel")
             .headerFilter("lastName", "state")
             .channel("outputChannel")
             .get();
}
<int:header-filter input-channel="inputChannel"
		output-channel="outputChannel" header-names="lastName, state"/>

正如你所见,对头过滤器进行配置非常简单。它是一个具有输入和输出通道以及 header-names 特性的典型端点。该特性接受需要移除的头名称(如果有多个则用逗号分隔)。因此,在前面的示例中,名为“lastName”和“state”的头在出站消息中不存在。

As you can see, configuration of a header filter is quite simple. It is a typical endpoint with input and output channels and a header-names attribute. That attribute accepts the names of the headers (delimited by commas if there are multiple) that need to be removed. So, in the preceding example, the headers named 'lastName' and 'state' are not present on the outbound message.

Codec-Based Transformers

有关更多信息,请参阅 Codec

See Codec.