此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Integration 6.5.1! |
转换器
消息转换器在实现消息生产者和消息消费者的松散耦合方面起着非常重要的作用。
您可以在这些组件之间添加转换器,而不是要求每个消息生成组件都知道下一个使用者期望什么类型。
通用转换器,例如将String
添加到 XML 文档,也是高度可重用的。
对于某些系统,最好提供规范数据模型,但 Spring Integration 的一般理念是不需要任何特定格式。 相反,为了获得最大的灵活性,Spring Integration 旨在提供最简单的扩展模型。 与其他端点类型一样,在 XML 或 Java 注释中使用声明性配置使简单的 POJO 能够适应消息转换器的角色。 本章的其余部分将介绍这些配置选项。
为了最大限度地提高灵活性,Spring 不需要基于 XML 的消息有效负载。 尽管如此,该框架确实提供了一些方便的转换器来处理基于 XML 的有效负载,如果这确实是您的应用程序的正确选择。 有关这些转换器的详细信息,请参阅 XML 支持 - 处理 XML 有效负载。 |
使用 Java 和其他 DSL 配置 Transformer
对于简单的 Java 和 Annotation 配置,Spring bean POJO 方法必须用@Transformer
注释,当从输入通道使用消息时,框架会调用它:
public class SomeService {
@Transformer(inputChannel = "transformChannel", outputChannel = "nextServiceChannel")
public OutputData exampleTransformer(InputData payload) {
...
}
}
请参阅注释支持中的更多信息。
对于 Java、Groovy 或 Kotlin DSL,则.transform()
运算符IntegrationFlow
表示 Transformer 端点:
-
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 的更多信息,请参阅相应章节:
使用 XML 配置 Transformer
这<transformer>
元素用于创建消息转换端点。
除了input-channel
和output-channel
属性,它需要一个ref
属性。
这ref
可以指向包含@Transformer
注释(请参阅使用注释配置 Transformer),或者它可以与method
属性。
<int:transformer id="testTransformer" ref="testTransformerBean" input-channel="inChannel"
method="transform" output-channel="outChannel"/>
<beans:bean id="testTransformerBean" class="org.foo.TestTransformer" />
使用ref
如果自定义 Transformer 处理程序实现可以在其他<transformer>
定义。
但是,如果自定义转换器处理程序实现的范围应限定为<transformer>
,您可以定义内部 bean 定义,如以下示例所示:
<int:transformer id="testTransformer" input-channel="inChannel" method="transform"
output-channel="outChannel">
<beans:bean class="org.foo.TestTransformer"/>
</transformer>
同时使用ref 属性和内部处理程序定义<transformer> 不允许配置,因为它会创建不明确的条件并导致抛出异常。 |
如果ref 属性引用扩展的 beanAbstractMessageProducingHandler (例如框架本身提供的 transformer),通过将输出通道直接注入处理程序来优化配置。
在这种情况下,每个ref 必须是单独的 bean 实例(或prototype -scoped bean)或使用<bean/> 配置类型。
如果您无意中从多个 Bean 引用了相同的消息处理程序,则会收到配置异常。 |
使用 POJO 时,用于转换的方法可能需要Message
类型或入站消息的有效负载类型。
它还可以通过使用@Header
和@Headers
参数注释。
该方法的返回值可以是任何类型。
如果返回值本身是Message
,传递到转换器的输出通道。
从 Spring Integration 2.0 开始,消息转换器的转换方法不能再返回null
.
返回null
导致异常,因为应始终期望消息转换器将每个源消息转换为有效的目标消息。
换句话说,不应将消息转换器用作消息过滤器,因为有一个专用的<filter>
选项。
但是,如果您确实需要这种类型的行为(组件可能会返回null
这不应被视为错误),您可以使用服务激活器。
其requires-reply
值为false
默认情况下,但可以设置为true
为了抛出异常null
返回值,与 transformer 一样。
Transformer 和 Spring 表达式语言 (SpEL)
与路由器、聚合器和其他组件一样,从 Spring Integration 2.0 开始,只要转换逻辑相对简单,transformer 也可以从 SpEL 支持中受益。 以下示例显示了如何使用 SpEL 表达式:
<int:transformer input-channel="inChannel"
output-channel="outChannel"
expression="payload.toUpperCase() + '- [' + T(System).currentTimeMillis() + ']'"/>
前面的示例在不编写自定义转换器的情况下转换有效负载。
我们的有效负载(假设是String
) 是大写的,与当前时间戳连接,并应用了一些格式。
普通转换器
Spring Integration 提供了一些 Transformer 实现。
对象到字符串转换器
因为使用toString()
表示Object
,Spring Integration 提供了一个ObjectToStringTransformer
(另请参阅Transformers
factory),其中输出是Message
使用 Stringpayload
.
那String
是调用toString()
对入站消息的有效负载进行作。
以下示例显示了如何声明对象到字符串转换器的实例:
-
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
Namespace。
而该通道适配器仅支持String
、byte-array 或java.io.File
有效负载,在适配器处理必要的转换之前立即添加此转换器。
只要结果toString()
call 是你想要写入文件的内容。
否则,您可以使用前面显示的通用“transformer”元素提供基于 POJO 的自定义 transformer。
调试时,通常不需要此转换器,因为logging-channel-adapter 能够记录消息有效负载。
有关更多详细信息,请参阅窃听。 |
对象到字符串的转换器非常简单。
它调用toString()
在入站有效负载上。
从 Spring Integration 3.0 开始,此规则有两个例外:
-
如果有效负载是
char[]
,它调用new String(payload)
. -
如果有效负载是
byte[]
,它调用new String(payload, charset)
哪里charset
默认为 UTF-8。 这charset
可以通过在 Transformer 上提供 charset 属性来修改。
为了更复杂(例如在运行时动态选择字符集),您可以使用基于 SpEL 表达式的转换器,如以下示例所示:
-
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 序列化,但您可以提供 Spring 的实现Serializer
或Deserializer
策略,使用serializer
和deserializer
属性。
另请参阅Transformers
工厂类。
以下示例演示如何使用 Spring 的序列化器和反序列化器:
-
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 包和类模式。
默认情况下,所有类都被反序列化。 |
Object
-自-Map
和Map
-自-Object
变形金刚
Spring Integration 还提供了Object
-自-Map
和Map
-自-Object
transformer,它使用 JSON 序列化和反序列化对象图。
对象层次结构被内省到最原始的类型(String
,int
,依此类推)。
这种类型的路径用 SpEL 描述,它成为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
允许您在不共享实际类型的情况下描述对象结构,这允许您将对象图还原并重建为不同类型的对象图,只要您保持该结构。
例如,可以使用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
}
如果您需要创建“结构化”地图,则可以提供flatten
属性。
默认值为 'true'。
如果将其设置为 'false',则结构是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 提供了相应的 XML 组件和 Java DSL 工厂:
-
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,如下所示:
-
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()
方法,如以下示例所示:
-
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,如以下示例所示:
-
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 被抛出。 |
从 5.0 版开始,您可以提供ObjectToMapTransformer
与定制的JsonObjectMapper
— 当您需要特殊格式的日期或空集合的空值(和其他用途)时。
有关 JSON 转换器的更多信息,请参阅 JSON 转换器JsonObjectMapper
实现。
流式转换器
这StreamTransformer
变换InputStream
有效负载到byte[]
( 或String
如果charset
提供)。
以下示例演示如何使用stream-transformer
元素:
-
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 -->
以下示例演示如何使用StreamTransformer
class 和@Transformer
注释以在 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 转换器
Spring Integration 提供对象到 JSON 和 JSON 到对象转换器。 以下一对示例演示了如何在 XML 中声明它们:
<int:object-to-json-transformer input-channel="objectMapperInput"/>
<int:json-to-object-transformer input-channel="objectMapperInput"
type="foo.MyDomainObject"/>
默认情况下,前面列表中的转换器使用普通JsonObjectMapper
.
它基于类路径中的实现。
您可以提供自己的定制JsonObjectMapper
使用适当的选项或基于所需库(例如 GSON)的实现,如以下示例所示:
<int:json-to-object-transformer input-channel="objectMapperInput"
type="something.MyDomainObject" object-mapper="customObjectMapper"/>
从 3.0 版开始, |
不妨考虑使用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 版开始, 如果您希望将 |
从 3.0 版开始,ObjectToJsonTransformer
将反映源类型的标头添加到邮件中。
同样,JsonToObjectTransformer
可以在将 JSON 转换为对象时使用这些类型标头。
这些标头映射在 AMQP 适配器中,以便它们与 Spring-AMQP 完全兼容JsonMessageConverter
.
这样,以无需任何特殊配置即可工作:
-
…→amqp-outbound-adapter---→
-
---→amqp-inbound-adapter→json-to-object-transformer→…
其中出站适配器配置了
JsonMessageConverter
并且入站适配器使用默认的SimpleMessageConverter
. -
…→object-to-json-transformer→amqp-outbound-adapter---→
-
---→amqp-inbound-adapter→…
其中出站适配器配置了
SimpleMessageConverter
并且入站适配器使用默认的JsonMessageConverter
. -
…→object-to-json-transformer→amqp-outbound-adapter---→
-
---→amqp-inbound-adapter→json-to-object-transformer→
如果两个适配器都配置了
SimpleMessageConverter
.
使用标头确定类型时,不应提供class 属性,因为它优先于标头。 |
除了 JSON Transformer 之外,Spring Integration 还提供了一个内置的#jsonPath
用于表达式的 SpEL 函数。
有关更多信息,请参阅 Spring 表达式语言 (SpEL)。
从 3.0 版本开始,Spring Integration 还提供了一个内置的#xpath
用于表达式的 SpEL 函数。
有关详细信息,请参阅 SpEL 函数 #xpath。
从 4.0 版开始,ObjectToJsonTransformer
支持resultType
属性,以指定节点 JSON 表示形式。
结果节点树表示取决于提供的JsonObjectMapper
.
默认情况下,ObjectToJsonTransformer
使用Jackson2JsonObjectMapper
并将对象到节点树的转换委托给ObjectMapper#valueToTree
方法。
节点 JSON 表示形式为使用JsonPropertyAccessor
当下游消息流使用 SpEL 表达式来访问 JSON 数据的属性时。
有关详细信息,请参阅属性访问器。
从 5.1 版开始,resultType
可以配置为BYTES
生成带有byte[]
有效负载,以便在使用使用此数据类型运行的下游处理程序时使用。
从 5.2 版开始,JsonToObjectTransformer
可以配置为ResolvableType
在使用目标 JSON 处理器反序列化期间支持泛型。
此外,此组件现在首先查询请求消息标头,以了解JsonHeaders.RESOLVABLE_TYPE
或JsonHeaders.TYPE_ID
否则回退到配置的类型。
这ObjectToJsonTransformer
now 还填充了JsonHeaders.RESOLVABLE_TYPE
标头,基于任何可能的下游方案的请求消息有效负载。
从 5.2.6 版本开始,JsonToObjectTransformer
可以提供valueTypeExpression
以解决ResolvableType
用于有效负载在运行时根据请求消息从 JSON 进行转换。
默认情况下,它会咨询JsonHeaders
在请求消息中。
如果此表达式返回null
或ResolvableType
架构物会抛出一个ClassNotFoundException
,转换器回退到提供的targetType
.
此逻辑作为表达式存在,因为JsonHeaders
可能没有实际的类值,而是一些类型 ID,这些类型 ID 必须根据某些外部注册表映射到目标类。
Apache Avro 转换器
5.2 版添加了简单的转换器来与 Apache Avro 进行转换。
它们并不复杂,因为没有模式注册表;转换器只需使用嵌入在SpecificRecord
从 Avro 架构生成的实现。
发送到SimpleToAvroTransformer
必须具有实现SpecificRecord
;转换器可以处理多种类型。
这SimpleFromAvroTransformer
必须配置SpecificRecord
类,用作反序列化的默认类型。
您还可以指定 SpEL 表达式,以使用setTypeExpression
方法。
默认的 SpEL 表达式为headers[avro_type]
(AvroHeaders.TYPE
),默认情况下,由SimpleToAvroTransformer
替换为源类的完全限定类名称。
如果表达式返回null
这defaultType
被使用。
这SimpleToAvroTransformer
还有一个setTypeExpression
方法。
这允许生产者和消费者的解耦,其中发送者可以将标头设置为表示该类型的某个Tokens,然后消费者将该Tokens映射到一个类型。
协议缓冲液转换器
版本 6.1 增加了对从协议缓冲区数据内容转换和到协议缓冲区数据内容转换的支持。
这ToProtobufTransformer
转换一个com.google.protobuf.Message
消息有效负载转换为本机字节数组或 JSON 文本有效负载。
这application/x-protobuf
内容类型(默认使用)生成字节数组输出有效负载。
如果内容类型是application/json
添加com.google.protobuf:protobuf-java-util
如果在类路径上找到,则输出为文本 JSON 有效负载。
如果未设置内容类型标头ToProtobufTransformer
默认为application/x-protobuf
.
这FromProtobufTransformer
将字节数组或文本 protobuf 有效负载(取决于内容类型)转换回com.google.protobuf.Message
实例。
这FromProtobufTransformer
应显式指定预期的类类型(使用setExpectedType
方法),或使用 SpEL 表达式使用setExpectedTypeExpression
方法。
默认的 SpEL 表达式为headers[proto_type]
(ProtoHeaders.TYPE
)由ToProtobufTransformer
替换为源的完全限定类名com.google.protobuf.Message
类。
例如,编译以下 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
类。
然后使用:
// 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);
使用注释配置 Transformer
您可以添加@Transformer
注释到期望Message
类型或消息有效负载类型。
返回值的处理方式与前面所述的完全相同在描述<transformer>
元素.
以下示例演示如何使用@Transformer
注释以转换String
变成一个Order
:
@Transformer
Order generateOrder(String productId) {
return new Order(productId);
}
Transformer 方法也可以接受@Header
和@Headers
注释,如Annotation Support
.
以下示例显示如何使用@Header
注解:
@Transformer
Order generateOrder(String productId, @Header("customerName") String customer) {
return new Order(productId, customer);
}
另请参阅使用注释为端点提供建议。
标头过滤器
有时,您的转换用例可能就像删除几个标头一样简单。 对于此类用例,Spring Integration提供了一个标头过滤器,可让您指定应从输出消息中删除的某些标头名称(例如,出于安全原因删除标头或仅暂时需要的值)。 基本上,标头过滤器与标头丰富器相反。 后者在 Header Enricher 中进行了讨论。 以下示例定义标头筛选器:
-
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”的标头不存在于出站邮件中。
基于编解码器的转换器
请参阅编解码器。