转换器
消息转换器在实现消息生成者和消息使用者的松散耦合方面起着非常重要的作用。
您可以在这些组件之间添加转换器,而不是要求每个消息生成组件都知道下一个使用者期望的类型。
泛型转换器(例如将 a 转换为 XML 文档的转换器)也是高度可重用的。String
对于某些系统,最好提供规范的数据模型,但 Spring 集成的一般理念是不需要任何特定的格式。 相反,为了获得最大的灵活性, Spring 集成旨在提供最简单的扩展模型。 与其他端点类型一样,在 XML 或 Java 注释中使用声明性配置使简单的 POJO 能够适应消息转换器的角色。 本章的其余部分介绍了这些配置选项。
为了最大限度地提高灵活性, Spring 不需要基于 XML 的消息有效负载。 尽管如此,如果这确实是您的应用程序的正确选择,那么该框架确实提供了一些方便的转换器来处理基于 XML 的有效负载。 有关这些转换器的更多信息,请参见 XML 支持 - 处理 XML 负载。 |
使用 Java 和其他 DSL 配置 Transformer
对于简单的Java和注释配置,Spring bean POJO方法必须用注释标记,当从输入通道消费消息时,框架会调用它:@Transformer
public class SomeService {
@Transformer(inputChannel = "transformChannel", outputChannel = "nextServiceChannel")
public OutputData exampleTransformer(InputData payload) {
...
}
}
有关更多信息,请参阅注释支持。
对于 Java、Groovy 或 Kotlin DSL,an 的运算符表示 transformer 端点:.transform()
IntegrationFlow
-
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
该元素用于创建消息转换终端节点。
除了 and 属性之外,它还需要一个 attribute。
该 可以指向包含单个方法上的 Comments 的对象(请参见使用 Comments 配置 Transformer),也可以与属性中提供的显式方法名称值组合。<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" />
如果自定义转换器处理程序实现可以在其他定义中重用,则通常建议使用属性。
但是,如果自定义转换器处理程序实现的范围应限定为单个定义的定义,则可以定义内部 Bean 定义,如下例所示:ref
<transformer>
<transformer>
<int:transformer id="testTransformer" input-channel="inChannel" method="transform"
output-channel="outChannel">
<beans:bean class="org.foo.TestTransformer"/>
</transformer>
不允许在同一配置中同时使用 attribute 和内部处理程序定义,因为它会创建不明确的条件并导致引发异常。ref <transformer> |
如果该属性引用了扩展的 bean(例如框架本身提供的转换器),则通过将输出通道直接注入处理程序来优化配置。
在这种情况下,每个都必须是单独的 bean 实例(或-scoped bean)或使用内部配置类型。
如果您无意中从多个 bean 引用了相同的消息处理程序,则会收到配置异常。ref AbstractMessageProducingHandler ref prototype <bean/> |
使用 POJO 时,用于转换的方法可能需要入站消息的类型或有效负载类型。
它还可以单独接受消息头值,也可以分别使用 和 parameter 注释作为完整 map 接受消息头值。
该方法的返回值可以是任何类型。
如果返回值本身是 ,则将其传递给 transformer 的 output channel。Message
@Header
@Headers
Message
从 Spring Integration 2.0 开始,消息转换器的 transformation method 不能再返回。
返回会导致异常,因为消息转换器应始终将每个源消息转换为有效的目标消息。
换句话说,消息转换器不应用作消息过滤器,因为有一个专用选项。
但是,如果您确实需要这种类型的行为(组件可能会返回,并且不应将其视为错误),则可以使用服务激活器。
其值是 default 的,但可以设置为该值,以便为返回值引发异常,就像 transformer 一样。null
null
<filter>
null
requires-reply
false
true
null
转换器和 Spring 表达式语言 (SpEL)
与路由器、聚合器和其他组件一样,从 Spring Integration 2.0 开始,只要转换逻辑相对简单,转换器也可以从 SPEL 支持中受益。 下面的示例展示了如何使用 SPEL 表达式:
<int:transformer input-channel="inChannel"
output-channel="outChannel"
expression="payload.toUpperCase() + '- [' + T(System).currentTimeMillis() + ']'"/>
前面的示例在不编写自定义转换器的情况下转换有效负载。
我们的有效负载(假设为 a )是大写的,与当前时间戳连接,并应用了一些格式。String
共 电 转换器
Spring 集成提供了一些 transformer 实现。
对象到字符串转换器
因为使用表示形式相当普遍,所以Spring Integration提供了一个(另请参见工厂),其中输出是带有String的 a 。
这是对入站 Message 的有效负载调用操作的结果。
下面的示例演示如何声明 object-to-string 转换器的实例:toString()
Object
ObjectToStringTransformer
Transformers
Message
payload
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"/>
这个转换器的一个潜在用途是将一些任意对象发送到命名空间中的 'outbound-channel-adapter'。
虽然该 channel adapter 默认仅支持 , byte-array 或 payloads,但在适配器处理必要的转换之前立即添加此 transformer 。
只要调用的结果是你想要写入文件的内容,就可以正常工作。
否则,您可以使用前面显示的通用 'transformer' 元素来提供基于 POJO 的自定义 transformer。file
String
java.io.File
toString()
调试时,通常不需要此转换器,因为 能够记录消息有效负载。
有关更多详细信息,请参见 Wire Tap。logging-channel-adapter |
对象到字符串转换器非常简单。
它对入站有效负载进行调用。
从 Spring Integration 3.0 开始,这条规则有两个例外:toString()
-
如果有效负载是 ,则它会调用 。
char[]
new String(payload)
-
如果有效负载是 ,则它会调用 ,其中默认为 UTF-8。 可以通过在 transformer 上提供 charset 属性来修改 。
byte[]
new String(payload, charset)
charset
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']" />
如果需要将 an 序列化为字节数组或将 byte 数组反序列化回 ,Spring Integration 提供了对称序列化转换器。
默认情况下,这些使用标准 Java 序列化,但您可以分别使用 和 属性来提供 Spring 或策略的实现。
另请参见 factory 类。
下面的示例展示了如何使用 Spring 的序列化器和反序列化器:Object
Object
Serializer
Deserializer
serializer
deserializer
Transformers
-
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.*"/>
当反序列化来自不受信任的来源的数据时,您应该考虑添加 of 包和类模式。
默认情况下,所有类都被反序列化。allow-list |
Object
转换器 - 到 - 和 - 转换器Map
Map
Object
Spring 集成还提供了 -to- 和 -to- 转换器,它们使用 JSON 来序列化和反序列化对象图。
对象层次结构被内省到最原始的类型 (、 和 等)。
此类型的路径用 SpEL 描述,它在转换后的 .
基元类型变为值。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 的 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
}
如果需要创建 “结构化” 映射,则可以提供 attribute。
默认值为 'true'。
如果将其设置为 'false',则结构是对象的。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 集成提供了相应的 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"/>
您还可以将 attribute 设置为 false,如下所示:flatten
-
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 集成为 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"/>
或者,你可以使用属性和原型范围的 bean,如下例所示:ref
-
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。
否则,将引发 a。BeanCreationException |
从版本 5.0 开始,你可以提供自定义的 — 当你需要特殊格式的日期或空集合的 null(和其他用途)时。
有关实施的更多信息,请参阅 JSON 转换器。ObjectToMapTransformer
JsonObjectMapper
JsonObjectMapper
Stream Transformer
将有效负载转换为 a ( 或 a 如果提供了 a)。StreamTransformer
InputStream
byte[]
String
charset
下面的示例演示如何在 XML 中使用该元素: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 -->
以下示例演示如何使用类和注释在 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 集成提供了 Object-to-JSON 和 JSON to-Object 转换器。 下面的一对示例演示如何在 XML 中声明它们:
<int:object-to-json-transformer input-channel="objectMapperInput"/>
<int:json-to-object-transformer input-channel="objectMapperInput"
type="foo.MyDomainObject"/>
默认情况下,前面清单中的 transformer 使用 vanilla .
它基于 Classpath 中的实现。
您可以使用适当的选项或基于所需的库(例如 GSON)提供自己的自定义实现,如下例所示:JsonObjectMapper
JsonObjectMapper
<int:json-to-object-transformer input-channel="objectMapperInput"
type="something.MyDomainObject" object-mapper="customObjectMapper"/>
从版本 3.0 开始,该属性引用新策略接口的实例:.
此抽象允许使用 JSON 映射器的多个实现。
提供了包装 Jackson 2 的实现,并在 Classpath 上检测到版本。
类分别为 。 |
您可能希望考虑使用 或 factory 方法来创建具有所需 trait 的 。
下面的示例展示了如何使用这样的工厂: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 开始,如果 input 消息还没有该 Headers,则默认情况下将 Headers 设置为 。 如果您希望将 Headers 设置为其他值或用某个值(包括 )显式覆盖任何现有 Headers,请使用 attribute 。
如果要禁止显示标头的设置,请将该属性设置为空字符串 ()。
这样做会导致消息没有 Headers,除非 Importing 消息中存在这样的 Header。 |
从版本 3.0 开始,向消息添加反映源类型的 Headers。
同样,在将 JSON 转换为对象时,可以使用这些类型的标头。
这些标头在 AMQP 适配器中映射,因此它们与 Spring-AMQP JsonMessageConverter
完全兼容。ObjectToJsonTransformer
JsonToObjectTransformer
这使以无需任何特殊配置即可工作:
-
…→amqp-outbound-adapter---→
-
---→amqp-inbound-adapter→json-to-object-transformer→…
其中出站适配器配置了 a,入站适配器使用默认的 .
JsonMessageConverter
SimpleMessageConverter
-
…→object-to-json-transformer→amqp-outbound-adapter---→
-
---→amqp-inbound-adapter→…
其中出站适配器配置了 a,入站适配器使用默认的 .
SimpleMessageConverter
JsonMessageConverter
-
…→object-to-json-transformer→amqp-outbound-adapter---→
-
---→amqp-inbound-adapter→json-to-object-transformer→
其中两个适配器都配置了 .
SimpleMessageConverter
使用标头确定类型时,不应提供属性,因为它优先于标头。class |
除了 JSON 转换器之外, Spring 集成还提供了一个内置的 SPEL 函数,用于表达式。
有关更多信息,请参阅 Spring 表达式语言 (SpEL)。#jsonPath
从版本 3.0 开始, Spring 集成还提供了一个内置的 SPEL 函数,用于表达式。
有关更多信息,请参阅 #xpath SPEL 函数。#xpath
从版本 4.0 开始,支持该属性来指定节点 JSON 表示形式。
结果节点树表示形式取决于提供的 .
默认情况下,使用 a 并将对象到节点树的转换委托给该方法。
节点 JSON 表示形式为下游消息流使用具有 JSON 数据属性访问权限的 SPEL 表达式提供了效率。
有关更多信息,请参见 Property Accessors 。ObjectToJsonTransformer
resultType
JsonObjectMapper
ObjectToJsonTransformer
Jackson2JsonObjectMapper
ObjectMapper#valueToTree
JsonPropertyAccessor
从版本 5.1 开始,可以配置为生成带有有效负载的消息,以便在使用使用此数据类型的下游处理程序时方便使用。resultType
BYTES
byte[]
从版本 5.2 开始,可以使用 配置为在使用目标 JSON 处理器进行反序列化期间支持泛型。
此外,此组件现在首先查询请求消息标头是否存在 or,否则回退到配置的类型。
现在,它还根据请求消息负载填充任何可能的下游场景的标头。JsonToObjectTransformer
ResolvableType
JsonHeaders.RESOLVABLE_TYPE
JsonHeaders.TYPE_ID
ObjectToJsonTransformer
JsonHeaders.RESOLVABLE_TYPE
从版本 5.2.6 开始,可以提供 a 来解析有效负载,以便在运行时针对请求消息从 JSON 转换。
默认情况下,它在请求消息中进行咨询。
如果此表达式返回或构建抛出 a ,则转换器将回退到提供的 .
此 logic 以表达式的形式存在,因为可能没有真正的类值,而是一些类型 ID,必须根据某些外部注册表映射到目标类。JsonToObjectTransformer
valueTypeExpression
ResolvableType
JsonHeaders
null
ResolvableType
ClassNotFoundException
targetType
JsonHeaders
Apache Avro 转换器
版本 5.2 添加了简单的转换器,用于与 Apache Avro 进行转换。
它们并不复杂,因为没有 schema 注册表;转换器只使用嵌入在从 Avro 架构生成的实现中的架构。SpecificRecord
发送到 的消息必须具有实现 ;transformer 可以处理多种类型。
必须使用一个类进行配置,该类用作反序列化的默认类型。
您还可以指定 SPEL 表达式来确定要使用该方法反序列化的类型。
默认的 SPEL 表达式是 () ,默认情况下,它由 填充源类的完全限定类名。
如果表达式返回 ,则使用 。SimpleToAvroTransformer
SpecificRecord
SimpleFromAvroTransformer
SpecificRecord
setTypeExpression
headers[avro_type]
AvroHeaders.TYPE
SimpleToAvroTransformer
null
defaultType
还有一个方法。
这允许生产者和使用者分离,发送者可以将标头设置为表示类型的某个令牌,然后使用者将该令牌映射到类型。SimpleToAvroTransformer
setTypeExpression
Protocol Buffers 转换器
版本 6.1 增加了对从 Protocol Buffers 数据内容转换和转换为 Protocol Buffers 数据内容的支持。
将消息负载转换为本机字节数组或 json 文本负载。
内容类型(默认使用)生成字节数组输出有效负载。
如果内容类型为 add the if found on the classpath,则输出为 text json payload。
如果未设置内容类型标头,则默认值为 。ToProtobufTransformer
com.google.protobuf.Message
application/x-protobuf
application/json
com.google.protobuf:protobuf-java-util
ToProtobufTransformer
application/x-protobuf
将字节数组或文本 protobuf 有效负载(取决于内容类型)转换回实例。
应该显式指定预期的类类型(使用该方法)或使用 SPEL 表达式来确定要使用该方法反序列化的类型。
默认的 SPEL 表达式是 () ,它由 填充 源类的完全限定类名。FromProtobufTransformer
com.google.protobuf.Message
FromProtobufTransformer
setExpectedType
setExpectedTypeExpression
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);
配置带有注释的转换器
您可以将注释添加到需要 type 或 message payload 类型的方法中。
返回值的处理方式与前面描述 <transformer>
元素的部分中描述的完全相同。
以下示例演示如何使用注释将 a 转换为 :@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 集成提供了一个 Headers 过滤器,它允许你指定应该从输出消息中删除的某些 Headers 名称(例如,出于安全原因删除 Headers 或仅临时需要的值)。 基本上,Header 过滤器与 Header Enricher 相反。 后者在 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"/>
如您所见,报头过滤器的配置非常简单。
它是具有输入和输出通道以及属性的典型终端节点。
该属性接受需要删除的 Headers 的名称(如果有多个,则用逗号分隔)。
因此,在前面的示例中,出站消息中不存在名为 'lastName' 和 'state' 的标头。header-names
基于编解码器的转换器
请参阅 编解码器。