此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring AMQP 3.2.0! |
消息转换器
这AmqpTemplate
还定义了几种发送和接收委托给MessageConverter
.
这MessageConverter
为每个方向提供一种方法:一种用于转换为Message
另一个用于从Message
.
请注意,当转换为Message
中,除了对象之外,您还可以提供属性。
这object
参数通常对应于 Message 正文。
下面的清单显示了MessageConverter
接口定义:
public interface MessageConverter {
Message toMessage(Object object, MessageProperties messageProperties)
throws MessageConversionException;
Object fromMessage(Message message) throws MessageConversionException;
}
相关的Message
-sending 方法。AmqpTemplate
比我们之前讨论的方法更简单,因为它们不需要Message
实例。
相反,MessageConverter
负责“创建”每个Message
通过将提供的对象转换为 Byte 数组Message
body 的 ID 中,然后添加任何提供的MessageProperties
.
下面的清单显示了各种方法的定义:
void convertAndSend(Object message) throws AmqpException;
void convertAndSend(String routingKey, Object message) throws AmqpException;
void convertAndSend(String exchange, String routingKey, Object message)
throws AmqpException;
void convertAndSend(Object message, MessagePostProcessor messagePostProcessor)
throws AmqpException;
void convertAndSend(String routingKey, Object message,
MessagePostProcessor messagePostProcessor) throws AmqpException;
void convertAndSend(String exchange, String routingKey, Object message,
MessagePostProcessor messagePostProcessor) throws AmqpException;
在接收方,只有两种方法:一种接受队列名称,另一种依赖于已设置的模板的 “queue” 属性。 下面的清单显示了这两种方法的定义:
Object receiveAndConvert() throws AmqpException;
Object receiveAndConvert(String queueName) throws AmqpException;
这MessageListenerAdapter 异步 Consumer 中提到的MessageConverter . |
SimpleMessageConverter
默认实现MessageConverter
策略称为SimpleMessageConverter
.
这是RabbitTemplate
如果未显式配置替代项。
它处理基于文本的内容、序列化的 Java 对象和字节数组。
从Message
如果输入的内容类型Message
以 “text” 开头(例如
“text/plain”),它还会检查 content-encoding 属性,以确定在将Message
body 字节数组转换为 JavaString
.
如果未在输入上设置 content-encoding 属性Message
,则默认使用 UTF-8 字符集。
如果需要覆盖该默认设置,则可以配置SimpleMessageConverter
,将其defaultCharset
属性,并将其注入到RabbitTemplate
实例。
如果输入的 content-type 属性值Message
设置为 “application/x-java-serialized-object” 时,SimpleMessageConverter
尝试将字节数组反序列化 (解除冻结) 为 Java 对象。
虽然这可能对简单的原型设计很有用,但我们不建议依赖 Java 序列化,因为它会导致生产者和消费者之间紧密耦合。
当然,它也排除了任何一方使用非 Java 系统的可能性。
由于 AMQP 是一种有线级协议,因此在此类限制下失去大部分优势将是很遗憾的。
在接下来的两节中,我们将探讨一些不依赖 Java 序列化的情况下传递丰富领域对象内容的替代方案。
对于所有其他内容类型,SimpleMessageConverter
返回Message
body 内容直接作为字节数组。
有关重要信息,请参阅 Java 反序列化。
SerializerMessageConverter
此转换器类似于SimpleMessageConverter
除了它可以与其他 Spring Framework 一起配置Serializer
和Deserializer
的 implementationsapplication/x-java-serialized-object
转换。
有关重要信息,请参阅 Java 反序列化。
Jackson2JsonMessage转换器
本节介绍如何使用Jackson2JsonMessageConverter
与Message
.
它包含以下部分:
转换为Message
如上一节所述,通常不建议依赖 Java 序列化。
JSON 是一种相当常见的替代方案,它在不同语言和平台之间更加灵活和可移植
(JavaScript 对象表示法)。
转换器可以在任何RabbitTemplate
实例来覆盖其对SimpleMessageConverter
违约。
这Jackson2JsonMessageConverter
使用com.fasterxml.jackson
2.x 库。
以下示例将Jackson2JsonMessageConverter
:
<bean class="org.springframework.amqp.rabbit.core.RabbitTemplate">
<property name="connectionFactory" ref="rabbitConnectionFactory"/>
<property name="messageConverter">
<bean class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter">
<!-- if necessary, override the DefaultClassMapper -->
<property name="classMapper" ref="customClassMapper"/>
</bean>
</property>
</bean>
如上所示,Jackson2JsonMessageConverter
使用DefaultClassMapper
默认情况下。
类型信息被添加到 (和检索)MessageProperties
.
如果入站消息不包含MessageProperties
,但您知道预期的类型,则
可以使用defaultType
属性,如下例所示:
<bean id="jsonConverterWithDefaultType"
class="o.s.amqp.support.converter.Jackson2JsonMessageConverter">
<property name="classMapper">
<bean class="org.springframework.amqp.support.converter.DefaultClassMapper">
<property name="defaultType" value="thing1.PurchaseOrder"/>
</bean>
</property>
</bean>
此外,您还可以根据TypeId
页眉。
以下示例显示了如何执行此作:
@Bean
public Jackson2JsonMessageConverter jsonMessageConverter() {
Jackson2JsonMessageConverter jsonConverter = new Jackson2JsonMessageConverter();
jsonConverter.setClassMapper(classMapper());
return jsonConverter;
}
@Bean
public DefaultClassMapper classMapper() {
DefaultClassMapper classMapper = new DefaultClassMapper();
Map<String, Class<?>> idClassMapping = new HashMap<>();
idClassMapping.put("thing1", Thing1.class);
idClassMapping.put("thing2", Thing2.class);
classMapper.setIdClassMapping(idClassMapping);
return classMapper;
}
现在,如果发送系统将标头设置为thing1
,则转换器会创建一个Thing1
object 等。
有关从非 Spring 应用程序转换消息的完整讨论,请参阅从非 Spring 应用程序接收 JSON 示例应用程序。
从版本 2.4.3 开始,转换器不会添加contentEncoding
message 属性(如果supportedMediaType
具有charset
参数;这也用于编码。
一种新方法setSupportedMediaType
已添加:
String utf16 = "application/json; charset=utf-16";
converter.setSupportedContentType(MimeTypeUtils.parseMimeType(utf16));
从Message
入站消息根据发送系统添加到 Headers 的类型信息转换为对象。
从版本 2.4.3 开始,如果没有contentEncoding
message 属性,则转换器将尝试检测charset
参数中的contentType
message 属性并使用它。
如果两者都不存在,则如果supportedMediaType
具有charset
参数,它将用于解码,并最终回退到defaultCharset
财产。
一种新方法setSupportedMediaType
已添加:
String utf16 = "application/json; charset=utf-16";
converter.setSupportedContentType(MimeTypeUtils.parseMimeType(utf16));
在 1.6 之前的版本中,如果类型信息不存在,则转换将失败。 从版本 1.6 开始,如果缺少类型信息,转换器将使用 Jackson 默认值(通常是 map)转换 JSON。
此外,从版本 1.6 开始,当您使用@RabbitListener
注解(在方法上),推断的类型信息将添加到MessageProperties
.
这允许转换器转换为目标方法的参数类型。
这仅适用于一个没有注释的参数或单个参数具有@Payload
注解。
类型的参数Message
在分析过程中被忽略。
默认情况下,推断的类型信息将覆盖入站TypeId 和创建的相关标头
通过发送系统。
这允许接收系统自动转换为不同的域对象。
仅当参数类型是具体的(不是抽象或接口)或来自java.util 包。
在所有其他情况下,TypeId 并使用 related headers。
在某些情况下,您可能希望覆盖默认行为并始终使用TypeId 信息。
例如,假设您有一个@RabbitListener 这需要Thing1 参数,但消息包含Thing2 那
是Thing1 (这是具体的)。
推断的类型将不正确。
要处理这种情况,请将TypePrecedence 属性Jackson2JsonMessageConverter 自TYPE_ID 相反
的默认值INFERRED .
(该属性实际上位于转换器的DefaultJackson2JavaTypeMapper ,但转换器上提供了 setter
为方便起见。
如果你注入了一个自定义类型 mapper,你应该在 mapper 上设置 property 。 |
从Message 、传入的MessageProperties.getContentType() 必须符合 JSON 标准 (contentType.contains("json") 用于检查)。
从版本 2.2 开始,application/json 如果没有contentType 属性,或者它具有默认值application/octet-stream .
要恢复到之前的行为(返回未转换的byte[] ),将转换器的assumeSupportedContentType property 设置为false .
如果内容类型不受支持,则WARN 日志消息Could not convert incoming message with content-type […] ,并且message.getBody() 按原样返回 — 作为byte[] .
因此,为了满足Jackson2JsonMessageConverter 要求,生产者必须添加contentType message 属性 — 例如,作为application/json 或text/x-json 或使用Jackson2JsonMessageConverter ,它会自动设置标题。
下面的清单显示了许多 converter 调用: |
@RabbitListener
public void thing1(Thing1 thing1) {...}
@RabbitListener
public void thing1(@Payload Thing1 thing1, @Header("amqp_consumerQueue") String queue) {...}
@RabbitListener
public void thing1(Thing1 thing1, o.s.amqp.core.Message message) {...}
@RabbitListener
public void thing1(Thing1 thing1, o.s.messaging.Message<Foo> message) {...}
@RabbitListener
public void thing1(Thing1 thing1, String bar) {...}
@RabbitListener
public void thing1(Thing1 thing1, o.s.messaging.Message<?> message) {...}
在前面清单中的前四种情况下,转换器尝试转换为Thing1
类型。
第五个示例无效,因为我们无法确定哪个参数应该接收消息有效负载。
在第六个示例中,由于 Jackson 默认类型是WildcardType
.
但是,您可以创建自定义转换器并使用targetMethod
message 属性来决定要转换的类型
JSON 到
只有当@RabbitListener 注解在方法级别声明。
具有类级别@RabbitListener ,则转换后的类型用于选择哪个@RabbitHandler 方法调用。
因此,基础设施提供了targetObject message 属性,您可以在自定义
converter 来确定类型。 |
从版本 1.6.11 开始,Jackson2JsonMessageConverter 因此,DefaultJackson2JavaTypeMapper (DefaultClassMapper ) 提供trustedPackages 选项来克服序列化小工具漏洞。
默认情况下,为了向后兼容,Jackson2JsonMessageConverter 信任所有软件包 — 即,它用于 option。* |
从版本 2.4.7 开始,转换器可以配置为返回Optional.empty()
如果Jackson返回null
反序列化消息体后。
这有助于@RabbitListener
s 接收 null 负载,以两种方式:
@RabbitListener(queues = "op.1")
void listen(@Payload(required = false) Thing payload) {
handleOptional(payload); // payload might be null
}
@RabbitListener(queues = "op.2")
void listen(Optional<Thing> optional) {
handleOptional(optional.orElse(this.emptyThing));
}
要启用此功能,请将setNullAsOptionalEmpty
自true
;什么时候false
(默认),则转换器将回退到原始消息正文 (byte[]
).
@Bean
Jackson2JsonMessageConverter converter() {
Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter();
converter.setNullAsOptionalEmpty(true);
return converter;
}
反序列化抽象类
在 2.2.8 版本之前,如果@RabbitListener
是一个抽象类(包括接口),转换器将回退到在 Headers 中查找类型信息,如果存在,则使用该信息;如果不存在,它将尝试创建 Abstract 类。
这会导致一个问题,当自定义ObjectMapper
,该 API 配置了一个自定义反序列化器来处理抽象类,但传入消息具有无效的类型 Headers。
从版本 2.2.8 开始,默认情况下保留之前的行为。如果你有这样的自定义ObjectMapper
并且您希望忽略 type headers,并始终使用推断的类型进行转换,请将alwaysConvertToInferredType
自true
.
这是为了实现向后兼容性并避免在转换失败时尝试转换的开销(使用标准的ObjectMapper
).
使用 Spring Data Projection 接口
从版本 2.2 开始,你可以将 JSON 转换为 Spring Data Projection 接口,而不是具体类型。 这允许对数据进行非常有选择性的低耦合绑定,包括从 JSON 文档中的多个位置查找值。 例如,可以将以下接口定义为消息有效负载类型:
interface SomeSample {
@JsonPath({ "$.username", "$.user.name" })
String getUsername();
}
@RabbitListener(queues = "projection")
public void projection(SomeSample in) {
String username = in.getUsername();
...
}
默认情况下,将使用访问器方法在接收到的 JSON 文档中查找属性名称作为字段。
这@JsonPath
expression 允许自定义值查找,甚至定义多个 JSON 路径表达式,以便从多个位置查找值,直到表达式返回实际值。
要启用此功能,请将useProjectionForInterfaces
自true
在消息转换器上。
您还必须添加spring-data:spring-data-commons
和com.jayway.jsonpath:json-path
添加到 class 路径。
当用作@RabbitListener
方法,接口类型会像往常一样自动传递给转换器。
从Message
跟RabbitTemplate
如前所述,类型信息在消息标头中传达,以帮助转换器从消息转换。
这在大多数情况下效果很好。
但是,当使用泛型类型时,它只能转换简单对象和已知的 “容器” 对象(列表、数组和映射)。
从版本 2.0 开始,Jackson2JsonMessageConverter
实现SmartMessageConverter
,这允许它与新的RabbitTemplate
采用ParameterizedTypeReference
论点。
这允许转换复杂的泛型类型,如以下示例所示:
Thing1<Thing2<Cat, Hat>> thing1 =
rabbitTemplate.receiveAndConvert(new ParameterizedTypeReference<Thing1<Thing2<Cat, Hat>>>() { });
从版本 2.1 开始,AbstractJsonMessageConverter 类已被删除。
它不再是Jackson2JsonMessageConverter .
它已被AbstractJackson2MessageConverter . |
MarshallingMessageConverter
另一个选项是MarshallingMessageConverter
.
它将 Spring OXM 库的Marshaller
和Unmarshaller
策略接口。
您可以在此处阅读有关该库的更多信息。
在配置方面,最常见的是只提供 constructor 参数,因为大多数Marshaller
还实施Unmarshaller
.
以下示例显示如何配置MarshallingMessageConverter
:
<bean class="org.springframework.amqp.rabbit.core.RabbitTemplate">
<property name="connectionFactory" ref="rabbitConnectionFactory"/>
<property name="messageConverter">
<bean class="org.springframework.amqp.support.converter.MarshallingMessageConverter">
<constructor-arg ref="someImplemenationOfMarshallerAndUnmarshaller"/>
</bean>
</property>
</bean>
Jackson2XmlMessageConverter
此类是在版本 2.1 中引入的,可用于将消息与 XML 相互转换。
双Jackson2XmlMessageConverter
和Jackson2JsonMessageConverter
具有相同的基类:AbstractJackson2MessageConverter
.
这AbstractJackson2MessageConverter class 来替换已删除的 class:AbstractJsonMessageConverter . |
这Jackson2XmlMessageConverter
使用com.fasterxml.jackson
2.x 库。
您可以按照与Jackson2JsonMessageConverter
,但它支持 XML 而不是 JSON。
以下示例将Jackson2JsonMessageConverter
:
<bean id="xmlConverterWithDefaultType"
class="org.springframework.amqp.support.converter.Jackson2XmlMessageConverter">
<property name="classMapper">
<bean class="org.springframework.amqp.support.converter.DefaultClassMapper">
<property name="defaultType" value="foo.PurchaseOrder"/>
</bean>
</property>
</bean>
有关更多信息,请参见Jackson2JsonMessageConverter。
从版本 2.2 开始,application/xml 如果没有contentType 属性,或者它具有默认值application/octet-stream .
要恢复到之前的行为(返回未转换的byte[] ),将转换器的assumeSupportedContentType property 设置为false . |
ContentTypeDelegatingMessageConverter
这个类是在版本 1.4.2 中引入的,它允许将MessageConverter
基于MessageProperties
.
默认情况下,它会委托给SimpleMessageConverter
如果没有contentType
property 或存在与任何已配置的转换器都不匹配的值。
以下示例将ContentTypeDelegatingMessageConverter
:
<bean id="contentTypeConverter" class="ContentTypeDelegatingMessageConverter">
<property name="delegates">
<map>
<entry key="application/json" value-ref="jsonMessageConverter" />
<entry key="application/xml" value-ref="xmlMessageConverter" />
</map>
</property>
</bean>
Java 反序列化
本节介绍如何反序列化 Java 对象。
从不受信任的来源反序列化 java 对象时可能存在漏洞。 如果您接受来自不受信任来源的邮件,并且 默认情况下,允许的列表为空,这意味着不会反序列化任何类。 您可以设置模式列表,例如 按顺序检查模式,直到找到匹配项。
如果没有匹配项,则 您可以使用 |
消息属性转换器
这MessagePropertiesConverter
strategy 接口用于在 Rabbit Client 之间进行转换BasicProperties
和 Spring AMQPMessageProperties
.
默认实现 (DefaultMessagePropertiesConverter
) 通常足以满足大多数目的,但如果需要,您可以实现自己的
默认属性转换器将BasicProperties
类型的元素LongString
自String
大小不大于1024
字节。
较大LongString
实例不会被转换(请参阅下一段)。
可以使用 constructor 参数覆盖此限制。
从版本 1.6 开始,长度超过长字符串限制(默认值:1024)的标头现在保留为LongString
实例由DefaultMessagePropertiesConverter
.
您可以通过getBytes[]
,toString()
或getStream()
方法。
以前,DefaultMessagePropertiesConverter
将此类标头“转换”为DataInputStream
(实际上它只是引用了LongString
实例的DataInputStream
).
在输出时,此标头未被转换(除了转换为 String — 例如java.io.DataInputStream@1d057a39
通过调用toString()
在溪流上)。
大型进货LongString
标头现在也可以在输出时正确 “转换” (默认情况下)。
提供了一个新的构造函数,允许您将转换器配置为像以前一样工作。 下面的清单显示了该方法的 Javadoc 注释和声明:
/**
* Construct an instance where LongStrings will be returned
* unconverted or as a java.io.DataInputStream when longer than this limit.
* Use this constructor with 'true' to restore pre-1.6 behavior.
* @param longStringLimit the limit.
* @param convertLongLongStrings LongString when false,
* DataInputStream when true.
* @since 1.6
*/
public DefaultMessagePropertiesConverter(int longStringLimit, boolean convertLongLongStrings) { ... }
同样从版本 1.6 开始,一个名为correlationIdString
已添加到MessageProperties
.
以前,在与BasicProperties
由 RabbitMQ 客户端使用,则不需要byte[] <→ String
执行 conversion 是因为MessageProperties.correlationId
是一个byte[]
但BasicProperties
使用String
.
(最终,RabbitMQ 客户端使用 UTF-8 将String
到字节以放入协议消息中)。
为了提供最大的向后兼容性,一个名为correlationIdPolicy
已添加到DefaultMessagePropertiesConverter
.
这需要DefaultMessagePropertiesConverter.CorrelationIdPolicy
enum 参数。
默认情况下,它设置为BYTES
,这将复制之前的行为。
对于入站消息:
-
STRING
:仅correlationIdString
属性已映射 -
BYTES
:仅correlationId
属性已映射 -
BOTH
:两个属性都已映射
对于出站消息:
-
STRING
:仅correlationIdString
属性已映射 -
BYTES
:仅correlationId
属性已映射 -
BOTH
:两个属性都被考虑在内,并且String
Property 优先
同样从版本 1.6 开始,入站deliveryMode
property 不再映射到MessageProperties.deliveryMode
.
它被映射到MessageProperties.receivedDeliveryMode
相反。
此外,入站userId
property 不再映射到MessageProperties.userId
.
它被映射到MessageProperties.receivedUserId
相反。
这些更改是为了避免这些属性的意外传播,如果相同MessageProperties
object 用于出站消息。
从版本 2.2 开始,DefaultMessagePropertiesConverter
转换 type 为Class<?>
用getName()
而不是toString()
;这避免了使用应用程序必须从toString()
表示法。
对于滚动升级,您可能需要更改使用者以理解这两种格式,直到所有创建器都升级为止。