还定义了几种用于发送和接收委托给 .
为每个方向提供了一个单一的方法:一个用于转换为 a,另一个用于从 .
请注意,在转换为 时,除了对象之外,还可以提供属性。
该参数通常对应于 Message 正文。
以下列表显示了接口定义:AmqpTemplate
MessageConverter
MessageConverter
Message
Message
Message
object
MessageConverter
public interface MessageConverter {
Message toMessage(Object object, MessageProperties messageProperties)
throws MessageConversionException;
Object fromMessage(Message message) throws MessageConversionException;
}
相关的 -sending 方法比我们之前讨论的方法更简单,因为它们不需要实例。
相反,负责通过将提供的对象转换为正文的字节数组,然后添加任何提供的对象来“创建”每个对象。
下面的列表显示了各种方法的定义:Message
AmqpTemplate
Message
MessageConverter
Message
Message
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 MessageConverter |
异步消费者中提到的也使用了 .MessageListenerAdapter MessageConverter |
SimpleMessageConverter
该策略的默认实现称为 。
这是实例使用的转换器,如果未显式配置替代项。
它处理基于文本的内容、序列化的 Java 对象和字节数组。MessageConverter
SimpleMessageConverter
RabbitTemplate
从Message
如果输入的内容类型以“text”开头(例如,
“text/plain”),它还会检查 content-encoding 属性,以确定将 body 字节数组转换为 Java 时要使用的字符集。
如果未在输入上设置 content-encoding 属性,则默认使用 UTF-8 字符集。
如果需要覆盖该默认设置,可以配置 的实例,设置其属性,并将其注入到实例中。Message
Message
String
Message
SimpleMessageConverter
defaultCharset
RabbitTemplate
如果输入的 content-type 属性值设置为“application/x-java-serialized-object”,则尝试将字节数组反序列化(重新冻结)为 Java 对象。
虽然这对于简单的原型设计可能很有用,但我们不建议依赖 Java 序列化,因为它会导致生产者和使用者之间的紧密耦合。
当然,它也排除了在两端使用非 Java 系统的可能性。
由于 AMQP 是一种线级协议,因此由于这些限制而失去大部分优势将是不幸的。
在接下来的两节中,我们将探讨一些在不依赖 Java 序列化的情况下传递丰富域对象内容的替代方法。Message
SimpleMessageConverter
对于所有其他内容类型,将正文内容直接作为字节数组返回。SimpleMessageConverter
Message
有关重要信息,请参阅 Java 反序列化。
SerializerMessageConverter
此转换器与 类似,只是它可以配置其他 Spring Framework 和用于转换的实现。SimpleMessageConverter
Serializer
Deserializer
application/x-java-serialized-object
有关重要信息,请参阅 Java 反序列化。
Jackson2JsonMessageConverter(杰克逊2Json消息转换器)
本节介绍如何使用 来转换 和 转换 。
它包含以下部分:Jackson2JsonMessageConverter
Message
转换为Message
如上一节所述,通常不建议依赖 Java 序列化。
JSON 是一种相当常见的替代方案,它跨不同语言和平台更加灵活和可移植
(JavaScript 对象表示法)。
可以在任何实例上配置转换器以覆盖其默认值的使用。
使用 2.x 库。
以下示例配置:RabbitTemplate
SimpleMessageConverter
Jackson2JsonMessageConverter
com.fasterxml.jackson
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;
}
现在,如果发送系统将标头设置为 ,则转换器将创建一个对象,依此类推。
有关从非 Spring 应用程序转换消息的完整讨论,请参阅从非 Spring 应用程序接收 JSON 示例应用程序。thing1
Thing1
从版本 2.4.3 开始,如果 具有参数,则转换器不会添加消息属性;这也用于编码。
添加了一个新方法:contentEncoding
supportedMediaType
charset
setSupportedMediaType
String utf16 = "application/json; charset=utf-16";
converter.setSupportedContentType(MimeTypeUtils.parseMimeType(utf16));
从Message
入站消息根据发送系统添加到标头的类型信息转换为对象。
从版本 2.4.3 开始,如果没有 message 属性,转换器将尝试检测 message 属性中的参数并使用该参数。
如果两者都不存在,则如果 具有参数,则该参数将用于解码,并最终回退到该属性。
添加了一个新方法:contentEncoding
charset
contentType
supportedMediaType
charset
defaultCharset
setSupportedMediaType
String utf16 = "application/json; charset=utf-16";
converter.setSupportedContentType(MimeTypeUtils.parseMimeType(utf16));
在 1.6 之前的版本中,如果不存在类型信息,则转换将失败。 从版本 1.6 开始,如果缺少类型信息,转换器将使用 Jackson 默认值(通常是映射)转换 JSON。
此外,从版本 1.6 开始,当您使用注释(在方法上)时,推断的类型信息将添加到 .
这允许转换器转换为目标方法的参数类型。
仅当存在一个没有注释的参数或具有注释的单个参数时,这才适用。
在分析过程中将忽略类型的参数。@RabbitListener
MessageProperties
@Payload
Message
默认情况下,推断的类型信息将覆盖创建的入站标头和相关标头
由发送系统。
这允许接收系统自动转换为不同的域对象。
仅当参数类型是具体的(不是抽象的或接口的)或来自包时,这才适用。
在所有其他情况下,将使用 和 相关的标头。
在某些情况下,您可能希望覆盖默认行为并始终使用该信息。
例如,假设您有一个接受参数,但消息包含一个
是 (具体的) 的子类。
推断的类型不正确。
若要处理这种情况,请将属性设置为
的默认值。
(该属性实际上位于转换器上,但在转换器上提供了一个二传手
为方便起见。
如果注入自定义类型映射器,则应改为在映射器上设置该属性。TypeId java.util TypeId TypeId @RabbitListener Thing1 Thing2 Thing1 TypePrecedence Jackson2JsonMessageConverter TYPE_ID INFERRED DefaultJackson2JavaTypeMapper |
从 转换时,传入必须符合 JSON(用于检查)。
从版本 2.2 开始,如果没有属性,或者它具有默认值,则假定为 。
要恢复到上一个行为(返回未转换的),请将转换器的属性设置为 。
如果不支持内容类型,则会发出日志消息 ,并按原样返回 — 作为 .
因此,为了满足使用者端的要求,生产者必须添加 message 属性,例如,as 或 或 by using ,它会自动设置标头。
以下列表显示了许多转换器调用:Message MessageProperties.getContentType() contentType.contains("json") application/json contentType application/octet-stream byte[] assumeSupportedContentType false WARN Could not convert incoming message with content-type […] message.getBody() byte[] Jackson2JsonMessageConverter contentType application/json text/x-json Jackson2JsonMessageConverter |
@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
WildcardType
但是,您可以创建自定义转换器并使用 message 属性来决定要转换的类型
JSON 到。targetMethod
只有在方法级别声明注释时,才能实现这种类型推理。
对于类级别,转换后的类型用于选择要调用的方法。
因此,基础结构提供了 message 属性,您可以在自定义中使用该属性
转换器以确定类型。@RabbitListener @RabbitListener @RabbitHandler targetObject |
从版本 1.6.11 开始,因此,() 提供了克服序列化小工具漏洞的选项。
默认情况下,为了向后兼容,信任所有包 - 也就是说,它用于该选项。Jackson2JsonMessageConverter DefaultJackson2JavaTypeMapper DefaultClassMapper trustedPackages Jackson2JsonMessageConverter * |
从版本 2.4.7 开始,转换器可以配置为在反序列化消息正文后 Jackson 返回时返回。
这有助于 s 以两种方式接收 null 有效负载:Optional.empty()
null
@RabbitListener
@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 之前,如果推断的 a 类型是抽象类(包括接口),则转换器将回退到在标头中查找类型信息,如果存在,则使用该信息;如果不存在,它将尝试创建抽象类。
当使用配置了自定义反序列化程序来处理抽象类的自定义项时,这会导致问题,但传入消息具有无效的类型标头。@RabbitListener
ObjectMapper
从版本 2.2.8 开始,默认情况下会保留以前的行为。如果您有这样的自定义,并且想要忽略类型标头,并且始终使用推断的类型进行转换,请将 设置为 。
这是向后兼容性所必需的,并且为了避免尝试转换失败时的开销(使用标准)。ObjectMapper
alwaysConvertToInferredType
true
ObjectMapper
使用 Spring 数据投影接口
从版本 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 文档中将属性名称查找为字段。
该表达式允许自定义值查找,甚至定义多个 JSON 路径表达式,以从多个位置查找值,直到表达式返回实际值。@JsonPath
要启用此功能,请在消息转换器上设置 to。
还必须将 和 添加到类路径中。useProjectionForInterfaces
true
spring-data:spring-data-commons
com.jayway.jsonpath:json-path
当用作方法的参数时,接口类型会像往常一样自动传递给转换器。@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 |
默认情况下,推断的类型信息将覆盖创建的入站标头和相关标头
由发送系统。
这允许接收系统自动转换为不同的域对象。
仅当参数类型是具体的(不是抽象的或接口的)或来自包时,这才适用。
在所有其他情况下,将使用 和 相关的标头。
在某些情况下,您可能希望覆盖默认行为并始终使用该信息。
例如,假设您有一个接受参数,但消息包含一个
是 (具体的) 的子类。
推断的类型不正确。
若要处理这种情况,请将属性设置为
的默认值。
(该属性实际上位于转换器上,但在转换器上提供了一个二传手
为方便起见。
如果注入自定义类型映射器,则应改为在映射器上设置该属性。TypeId java.util TypeId TypeId @RabbitListener Thing1 Thing2 Thing1 TypePrecedence Jackson2JsonMessageConverter TYPE_ID INFERRED DefaultJackson2JavaTypeMapper |
从 转换时,传入必须符合 JSON(用于检查)。
从版本 2.2 开始,如果没有属性,或者它具有默认值,则假定为 。
要恢复到上一个行为(返回未转换的),请将转换器的属性设置为 。
如果不支持内容类型,则会发出日志消息 ,并按原样返回 — 作为 .
因此,为了满足使用者端的要求,生产者必须添加 message 属性,例如,as 或 或 by using ,它会自动设置标头。
以下列表显示了许多转换器调用:Message MessageProperties.getContentType() contentType.contains("json") application/json contentType application/octet-stream byte[] assumeSupportedContentType false WARN Could not convert incoming message with content-type […] message.getBody() byte[] Jackson2JsonMessageConverter contentType application/json text/x-json Jackson2JsonMessageConverter |
只有在方法级别声明注释时,才能实现这种类型推理。
对于类级别,转换后的类型用于选择要调用的方法。
因此,基础结构提供了 message 属性,您可以在自定义中使用该属性
转换器以确定类型。@RabbitListener @RabbitListener @RabbitHandler targetObject |
从版本 1.6.11 开始,因此,() 提供了克服序列化小工具漏洞的选项。
默认情况下,为了向后兼容,信任所有包 - 也就是说,它用于该选项。Jackson2JsonMessageConverter DefaultJackson2JavaTypeMapper DefaultClassMapper trustedPackages Jackson2JsonMessageConverter * |
从版本 2.1 开始,该类已被删除。
它不再是 的基类。
它已被替换为 。AbstractJsonMessageConverter Jackson2JsonMessageConverter AbstractJackson2MessageConverter |
MarshallingMessageConverter
另一个选项是 .
它委托给 Spring OXM 库的 和 策略接口的实现。
您可以在此处阅读有关该库的更多信息。
在配置方面,最常见的是只提供构造函数参数,因为大多数实现 也实现 .
以下示例演示如何配置:MarshallingMessageConverter
Marshaller
Unmarshaller
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 转换为 XML 和向 XML 转换消息。
两者具有相同的基类:.Jackson2XmlMessageConverter
Jackson2JsonMessageConverter
AbstractJackson2MessageConverter
引入该类是为了替换已删除的类:。AbstractJackson2MessageConverter AbstractJsonMessageConverter |
使用 2.x 库。Jackson2XmlMessageConverter
com.fasterxml.jackson
您可以像 一样使用它,只是它支持 XML 而不是 JSON。
以下示例配置:Jackson2JsonMessageConverter
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 false |
引入该类是为了替换已删除的类:。AbstractJackson2MessageConverter AbstractJsonMessageConverter |
从版本 2.2 开始,如果没有属性,或者它具有默认值,则假定为 。
要恢复到上一个行为(返回未转换的),请将转换器的属性设置为 。application/xml contentType application/octet-stream byte[] assumeSupportedContentType false |
ContentTypeDelegatingMessageConverter
此类是在 1.4.2 版中引入的,它允许根据 .
默认情况下,如果没有属性或存在与配置的转换器不匹配的值,则将其委托给 a。
以下示例配置:MessageConverter
MessageProperties
SimpleMessageConverter
contentType
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 对象时可能存在漏洞。 如果接受来自不受信任的来源的邮件,则应
请考虑配置允许反序列化的包和类。
这适用于 和 当它被配置为使用隐式或通过配置时。 默认情况下,允许列表为空,这意味着不会反序列化任何类。 您可以设置模式列表,例如 、 按顺序检查模式,直到找到匹配项。
如果没有匹配项,则抛出 a。 您可以使用这些转换器上的属性来设置模式。
或者,如果信任所有消息发起方,则可以将环境变量或系统属性设置为 。 |
从不受信任的来源反序列化 Java 对象时可能存在漏洞。 如果接受来自不受信任的来源的邮件,则应
请考虑配置允许反序列化的包和类。
这适用于 和 当它被配置为使用隐式或通过配置时。 默认情况下,允许列表为空,这意味着不会反序列化任何类。 您可以设置模式列表,例如 、 按顺序检查模式,直到找到匹配项。
如果没有匹配项,则抛出 a。 您可以使用这些转换器上的属性来设置模式。
或者,如果信任所有消息发起方,则可以将环境变量或系统属性设置为 。 |
消息属性转换器
策略接口用于在 Rabbit Client 和 Spring AMQP 之间进行转换。
默认实现 () 通常足以满足大多数目的,但如果需要,您可以实现自己的实现。
默认属性转换器在大小不大于字节时将类型的元素转换为实例。
较大的实例不会转换(请参阅下一段)。
可以使用构造函数参数覆盖此限制。MessagePropertiesConverter
BasicProperties
MessageProperties
DefaultMessagePropertiesConverter
BasicProperties
LongString
String
1024
LongString
从版本 1.6 开始,长于长字符串限制(默认值:1024)的标头现在默认由 .
您可以通过 、 或 方法访问内容。LongString
DefaultMessagePropertiesConverter
getBytes[]
toString()
getStream()
以前,将此类标头“转换”为 a(实际上它只是引用了实例的 )。
在输出时,此标头未转换(除了转换为字符串 — 例如,通过调用流)。DefaultMessagePropertiesConverter
DataInputStream
LongString
DataInputStream
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 开始,一个名为 的新属性已添加到 中。
以前,在 RabbitMQ 客户端使用的之间进行转换时,会执行不必要的转换,因为 是 ,但使用 .
(最终,RabbitMQ 客户端使用 UTF-8 将 转换为字节以放入协议消息中)。correlationIdString
MessageProperties
BasicProperties
byte[] <→ String
MessageProperties.correlationId
byte[]
BasicProperties
String
String
为了提供最大的向后兼容性,已将一个名为 的新属性添加到 .
这需要一个枚举参数。
默认情况下,它设置为 ,这将复制以前的行为。correlationIdPolicy
DefaultMessagePropertiesConverter
DefaultMessagePropertiesConverter.CorrelationIdPolicy
BYTES
对于入站消息:
-
STRING
:仅映射属性correlationIdString
-
BYTES
:仅映射属性correlationId
-
BOTH
:映射两个属性
对于出站消息:
-
STRING
:仅映射属性correlationIdString
-
BYTES
:仅映射属性correlationId
-
BOTH
:考虑两个属性,以属性为准String
同样从版本 1.6 开始,入站属性不再映射到 。
它被映射到。
此外,入站属性不再映射到 。
它被映射到。
这些更改是为了避免在将同一对象用于出站消息时意外传播这些属性。deliveryMode
MessageProperties.deliveryMode
MessageProperties.receivedDeliveryMode
userId
MessageProperties.userId
MessageProperties.receivedUserId
MessageProperties
从版本 2.2 开始,将任何自定义标头转换为 using 而不是 ;这样就避免了使用应用程序必须从表示形式中解析类名。
对于滚动升级,您可能需要更改使用者以了解这两种格式,直到所有生产者都升级为止。DefaultMessagePropertiesConverter
Class<?>
getName()
toString()
toString()