Message Converters

AmqpTemplate 还为发送和接收消息定义了几个委托给 MessageConverter 的方法。MessageConverter 为每个方向提供一种方法:一种用于转换为 Message,另一种用于从 Message 转换。请注意,在转换为 Message 时,除了对象外,还可以提供属性。object 参数通常对应于消息主体。以下清单显示了 MessageConverter 接口定义:

The AmqpTemplate also defines several methods for sending and receiving messages that delegate to a MessageConverter. The MessageConverter provides a single method for each direction: one for converting to a Message and another for converting from a Message. Notice that, when converting to a Message, you can also provide properties in addition to the object. The object parameter typically corresponds to the Message body. The following listing shows the MessageConverter interface definition:

public interface MessageConverter {

    Message toMessage(Object object, MessageProperties messageProperties)
            throws MessageConversionException;

    Object fromMessage(Message message) throws MessageConversionException;

}

AmqpTemplate 上相关的 Message 发送方法比我们之前讨论的方法更简单,因为它们不需要 Message 实例。相反,MessageConverter 负责通过将提供对象转换为 Message 主体的字节数组,然后添加任何提供的 MessageProperties 来“创建”每个 Message。以下清单显示了各种方法的定义:

The relevant Message-sending methods on the AmqpTemplate are simpler than the methods we discussed previously, because they do not require the Message instance. Instead, the MessageConverter is responsible for “creating” each Message by converting the provided object to the byte array for the Message body and then adding any provided MessageProperties. The following listing shows the definitions of the various methods:

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”属性已被设置。以下清单显示了这两个方法的定义:

On the receiving side, there are only two methods: one that accepts the queue name and one that relies on the template’s “queue” property having been set. The following listing shows the definitions of the two methods:

Object receiveAndConvert() throws AmqpException;

Object receiveAndConvert(String queueName) throws AmqpException;

Asynchronous Consumer 中提到的 MessageListenerAdapter 也使用 MessageConverter

The MessageListenerAdapter mentioned in Asynchronous Consumer also uses a MessageConverter.

SimpleMessageConverter

MessageConverter 策略的默认实现称为 SimpleMessageConverter。这是 RabbitTemplate 实例在你未显式配置替代项时使用的转换器。它可以处理基于文本的内容、序列化的 Java 对象和字节数组。

The default implementation of the MessageConverter strategy is called SimpleMessageConverter. This is the converter that is used by an instance of RabbitTemplate if you do not explicitly configure an alternative. It handles text-based content, serialized Java objects, and byte arrays.

Converting From a Message

如果输入 Message 的内容类型以“text”开头(例如,“text/plain”),它还会检查内容编码属性,以确定在将 Message 主体字节数组转换为 Java String 时要使用的字符集。如果在输入 Message 上未设置内容编码属性,则它默认使用 UTF-8 字符集。如果你需要覆盖该默认设置,你可以配置 SimpleMessageConverter 实例、设置其 defaultCharset 属性并将其注入 RabbitTemplate 实例。

If the content type of the input Message begins with "text" (for example, "text/plain"), it also checks for the content-encoding property to determine the charset to be used when converting the Message body byte array to a Java String. If no content-encoding property had been set on the input Message, it uses the UTF-8 charset by default. If you need to override that default setting, you can configure an instance of SimpleMessageConverter, set its defaultCharset property, and inject that into a RabbitTemplate instance.

如果输入 Message 的内容类型属性值设置为“application/x-java-serialized-object”,则 SimpleMessageConverter 尝试将字节数组反序列化(rehydrate)为 Java 对象。虽然这对于简单的原型制作可能很有用,但我们不建议依赖 Java 序列化,因为它会导致生产者和消费者之间紧密耦合。当然,它还排除了在任何一方使用非 Java 系统。由于 AMQP 是一个线缆级协议,因此使用此类限制会失去很多优势,这是非常不幸的。在接下来的两节中,我们将探讨一些在不依赖于 Java 序列化的前提下传递丰富域对象内容的备选方案。

If the content-type property value of the input Message is set to "application/x-java-serialized-object", the SimpleMessageConverter tries to deserialize (rehydrate) the byte array into a Java object. While that might be useful for simple prototyping, we do not recommend relying on Java serialization, since it leads to tight coupling between the producer and the consumer. Of course, it also rules out usage of non-Java systems on either side. With AMQP being a wire-level protocol, it would be unfortunate to lose much of that advantage with such restrictions. In the next two sections, we explore some alternatives for passing rich domain object content without relying on Java serialization.

对于所有其他内容类型,SimpleMessageConverterMessage 主体内容直接作为字节数组返回。

For all other content-types, the SimpleMessageConverter returns the Message body content directly as a byte array.

有关重要信息,请参见 Java Deserialization

See Java Deserialization for important information.

Converting To a Message

在从任意 Java 对象转换为 Message 时,SimpleMessageConverter 同样处理字节数组、字符串和可序列化实例。它将这些内容中的每一项都转换为字节(对于字节数组,无需转换),并相应地设置内容类型属性。如果要转换的 Object 与这些类型中的任何一种不匹配,则 Message 主体为 null。

When converting to a Message from an arbitrary Java Object, the SimpleMessageConverter likewise deals with byte arrays, strings, and serializable instances. It converts each of these to bytes (in the case of byte arrays, there is nothing to convert), and it sets the content-type property accordingly. If the Object to be converted does not match one of those types, the Message body is null.

SerializerMessageConverter

此转换器类似于 SimpleMessageConverter,只不过它可以针对用于 application/x-java-serialized-object 转换的其它 Spring Framework SerializerDeserializer 实现进行配置。

This converter is similar to the SimpleMessageConverter except that it can be configured with other Spring Framework Serializer and Deserializer implementations for application/x-java-serialized-object conversions.

有关重要信息,请参见 Java Deserialization

See Java Deserialization for important information.

Jackson2JsonMessageConverter

本节涵盖使用 Jackson2JsonMessageConverter 转换到 Message 和从 Message 转换。它包含以下部分:

This section covers using the Jackson2JsonMessageConverter to convert to and from a Message. It has the following sections:

Converting to a Message

如前一节所述,通常不建议依赖 Java 序列化。一种更灵活且更便于在不同语言和平台上移植的常见备选方案是 JSON(JavaScript 对象表示法)。可以在任何 RabbitTemplate 实例上配置转换器,以覆盖其对 SimpleMessageConverter 默认值的用法。Jackson2JsonMessageConverter 使用 com.fasterxml.jackson 2.x 库。以下示例配置了 Jackson2JsonMessageConverter

As mentioned in the previous section, relying on Java serialization is generally not recommended. One rather common alternative that is more flexible and portable across different languages and platforms is JSON (JavaScript Object Notation). The converter can be configured on any RabbitTemplate instance to override its usage of the SimpleMessageConverter default. The Jackson2JsonMessageConverter uses the com.fasterxml.jackson 2.x library. The following example configures a 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 属性配置静态类型,如下例所示:

As shown above, Jackson2JsonMessageConverter uses a DefaultClassMapper by default. Type information is added to (and retrieved from) MessageProperties. If an inbound message does not contain type information in MessageProperties, but you know the expected type, you can configure a static type by using the defaultType property, as the following example shows:

<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 中的值提供自定义映射。以下示例演示如何执行此操作:

In addition, you can provide custom mappings from the value in the TypeId header. The following example shows how to do so:

@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 对象,依此类推。请参阅 Receiving JSON from Non-Spring Applications 的示例应用程序,以获取有关从非 Spring 应用程序转换消息的完整讨论。

Now, if the sending system sets the header to thing1, the converter creates a Thing1 object, and so on. See the Receiving JSON from Non-Spring Applications sample application for a complete discussion about converting messages from non-Spring applications.

从版本 2.4.3 开始,如果 supportedMediaTypecharset 参数,转换器将不会添加 contentEncoding 消息属性;这也用于编码。已经添加了一个新方法 setSupportedMediaType

Starting with version 2.4.3, the converter will not add a contentEncoding message property if the supportedMediaType has a charset parameter; this is also used for the encoding. A new method setSupportedMediaType has been added:

String utf16 = "application/json; charset=utf-16";
converter.setSupportedContentType(MimeTypeUtils.parseMimeType(utf16));

Converting from a Message

入站消息根据发送系统添加到标头中的类型信息转换为对象。

Inbound messages are converted to objects according to the type information added to headers by the sending system.

从版本 2.4.3 开始,如果没有 contentEncoding 消息属性,转换器将尝试在 contentType 消息属性中检测 charset 参数并使用它。如果都不存在,如果 supportedMediaTypecharset 参数,它将用于解码,最终回退到 defaultCharset 属性。已经添加了一个新方法 setSupportedMediaType

Starting with version 2.4.3, if there is no contentEncoding message property, the converter will attempt to detect a charset parameter in the contentType message property and use that. If neither exist, if the supportedMediaType has a charset parameter, it will be used for decoding, with a final fallback to the defaultCharset property. A new method setSupportedMediaType has been added:

String utf16 = "application/json; charset=utf-16";
converter.setSupportedContentType(MimeTypeUtils.parseMimeType(utf16));

在 1.6 版本之前,如果不存在类型信息,转换将会失败。从 1.6 版本开始,如果缺少类型信息,转换器将使用 Jackson 默认值(通常是一个映射)将 JSON 转换。

In versions prior to 1.6, if type information is not present, conversion would fail. Starting with version 1.6, if type information is missing, the converter converts the JSON by using Jackson defaults (usually a map).

另外,从 1.6 版本开始,当您使用 @RabbitListener 注解(在方法上)时,推断出的类型信息将被添加到 MessageProperties 中。这使转换器能够转换为目标方法的参数类型。这仅适用于没有注解的一个参数,或者用 @Payload 注解的单个参数。在分析过程中,将忽略类型为 Message 的参数。

Also, starting with version 1.6, when you use @RabbitListener annotations (on methods), the inferred type information is added to the MessageProperties. This lets the converter convert to the argument type of the target method. This only applies if there is one parameter with no annotations or a single parameter with the @Payload annotation. Parameters of type Message are ignored during the analysis.

默认情况下,推断出的类型信息将覆盖传入的 TypeId 和发送系统创建的相关标头。这使得接收系统可以自动转换为不同的域对象。仅当参数类型是具体的(不是抽象的或接口)或来自 java.util 包时,此规则才适用。在所有其他情况下,将使用 TypeId 和相关标头。在某些情况下,您可能希望覆盖默认行为并始终使用 TypeId 信息。例如,假设您有一个 @RabbitListener,它接受 Thing1 参数,但消息中包含的是 Thing2(它是 Thing1 的子类)(具体)。推断出的类型将不正确。为了处理这种情况,请将 Jackson2JsonMessageConverter 上的 TypePrecedence 属性设置为 TYPE_ID,而不是默认的 INFERRED。(该属性实际上在转换器的 DefaultJackson2JavaTypeMapper 上,但为方便起见在转换器上提供了一个 setter。)如果您注入自定义类型映射器,则应转而设置映射器上的属性。

By default, the inferred type information will override the inbound TypeId and related headers created by the sending system. This lets the receiving system automatically convert to a different domain object. This applies only if the parameter type is concrete (not abstract or an interface) or it is from the java.util package. In all other cases, the TypeId and related headers is used. There are cases where you might wish to override the default behavior and always use the TypeId information. For example, suppose you have a @RabbitListener that takes a Thing1 argument but the message contains a Thing2 that is a subclass of Thing1 (which is concrete). The inferred type would be incorrect. To handle this situation, set the TypePrecedence property on the Jackson2JsonMessageConverter to TYPE_ID instead of the default INFERRED. (The property is actually on the converter’s DefaultJackson2JavaTypeMapper, but a setter is provided on the converter for convenience.) If you inject a custom type mapper, you should set the property on the mapper instead.

Message 转换成时,传入的 MessageProperties.getContentType() 必须符合 JSON(contentType.contains("json") 用于检查)。从 2.2 版本开始,如果不存在 contentType 属性或属性具有默认值 application/octet-stream,则假定为 application/json。要恢复为之前的行为(返回未转换的 byte[]),请将转换器的 assumeSupportedContentType 属性设置为 false。如果不支持内容类型,将会发出 WARN 日志消息 Could not convert incoming message with content-type […​],并将 message.getBody() 原样返回——作为 byte[]。因此,为了满足消费者端的 Jackson2JsonMessageConverter 要求,生产者必须添加 contentType 消息属性——例如,作为 application/jsontext/x-json,或者使用 Jackson2JsonMessageConverter,该属性会自动设置标题。以下清单显示了一些转换器调用:

When converting from the Message, an incoming MessageProperties.getContentType() must be JSON-compliant (contentType.contains("json") is used to check). Starting with version 2.2, application/json is assumed if there is no contentType property, or it has the default value application/octet-stream. To revert to the previous behavior (return an unconverted byte[]), set the converter’s assumeSupportedContentType property to false. If the content type is not supported, a WARN log message Could not convert incoming message with content-type […​], is emitted and message.getBody() is returned as is — as a byte[]. So, to meet the Jackson2JsonMessageConverter requirements on the consumer side, the producer must add the contentType message property — for example, as application/json or text/x-json or by using the Jackson2JsonMessageConverter, which sets the header automatically. The following listing shows a number of converter calls:

@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,因此应用 Jackson 默认值。

In the first four cases in the preceding listing, the converter tries to convert to the Thing1 type. The fifth example is invalid because we cannot determine which argument should receive the message payload. With the sixth example, the Jackson defaults apply due to the generic type being a WildcardType.

不过,您可以创建自定义转换器并使用 targetMethod 消息属性来决定将 JSON 转换为哪种类型。

You can, however, create a custom converter and use the targetMethod message property to decide which type to convert the JSON to.

只有在方法级别声明 @RabbitListener 注解时,才能实现此类型推断。通过类级 @RabbitListener,转换的类型用于选择要调用的 @RabbitHandler 方法。出于此原因,基础设施提供了 targetObject 消息属性,您可以在自定义转换器中使用此属性来确定类型。

This type inference can only be achieved when the @RabbitListener annotation is declared at the method level. With class-level @RabbitListener, the converted type is used to select which @RabbitHandler method to invoke. For this reason, the infrastructure provides the targetObject message property, which you can use in a custom converter to determine the type.

从 1.6.11 版本开始,Jackson2JsonMessageConverter 及其 DefaultJackson2JavaTypeMapper (DefaultClassMapper) 提供了 trustedPackages 选项,用于克服 Serialization Gadgets 漏洞。对于向后兼容性,Jackson2JsonMessageConverter 默认情况下信任所有包——也就是说,它对该选项使用 *

Starting with version 1.6.11, Jackson2JsonMessageConverter and, therefore, DefaultJackson2JavaTypeMapper (DefaultClassMapper) provide the trustedPackages option to overcome Serialization Gadgets vulnerability. By default and for backward compatibility, the Jackson2JsonMessageConverter trusts all packages — that is, it uses * for the option.

从 2.4.7 版本开始,如果 Jackson在反序列化消息体后返回 null,则可以配置转换器以返回 Optional.empty()。这为 @RabbitListener 提供了接收空负载的两种方式:

Starting with version 2.4.7, the converter can be configured to return Optional.empty() if Jackson returns null after deserializing the message body. This facilitates @RabbitListener s to receive null payloads, in two ways:

@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[])。

To enable this feature, set setNullAsOptionalEmpty to true; when false (default), the converter falls back to the raw message body (byte[]).

@Bean
Jackson2JsonMessageConverter converter() {
    Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter();
    converter.setNullAsOptionalEmpty(true);
    return converter;
}

Deserializing Abstract Classes

在 2.2.8 版本之前,如果 @RabbitListener 的推断类型是抽象类(包括接口),则转换器将回退到标头中查找类型信息,如果存在,则会使用该信息;如果不存在,它将尝试创建抽象类。在使用了配置有自定义反序列化程序来处理抽象类的自定义 ObjectMapper 时,但传入消息具有无效类型标头时,这会引起问题。

Prior to version 2.2.8, if the inferred type of a @RabbitListener was an abstract class (including interfaces), the converter would fall back to looking for type information in the headers and, if present, used that information; if that was not present, it would try to create the abstract class. This caused a problem when a custom ObjectMapper that is configured with a custom deserializer to handle the abstract class is used, but the incoming message has invalid type headers.

从 2.2.8 版本开始,将默认保留以前的行为。如果您有这样的自定义 ObjectMapper,并且您想忽略类型标头,并始终使用推断的类型进行转换,则将 alwaysConvertToInferredType 设置为 true。这对于向后兼容性和避免在失败时尝试转换(使用标准 ObjectMapper)的开销是必要的。

Starting with version 2.2.8, the previous behavior is retained by default. If you have such a custom ObjectMapper and you want to ignore type headers, and always use the inferred type for conversion, set the alwaysConvertToInferredType to true. This is needed for backwards compatibility and to avoid the overhead of an attempted conversion when it would fail (with a standard ObjectMapper).

Using Spring Data Projection Interfaces

从 2.2 版本开始,您可以将 JSON 转换为 Spring Data 投影接口,而不是具体类型。这允许对数据进行非常有选择性和低耦合的绑定,包括从 JSON 文档中的多个位置查找值。例如,可以将以下接口定义为消息有效负载类型:

Starting with version 2.2, you can convert JSON to a Spring Data Projection interface instead of a concrete type. This allows very selective, and low-coupled bindings to data, including the lookup of values from multiple places inside the JSON document. For example the following interface can be defined as message payload type:

interface SomeSample {

  @JsonPath({ "$.username", "$.user.name" })
  String getUsername();

}
@RabbitListener(queues = "projection")
public void projection(SomeSample in) {
    String username = in.getUsername();
    ...
}

默认情况下,访问器方法将用于在接收的 JSON 文档中作为字段查找属性名称。@JsonPath 表达式允许对值查找进行自定义,甚至可以定义多个 JSON 路径表达式,以从多个位置查找值,直到表达式返回实际值。

Accessor methods will be used to lookup the property name as field in the received JSON document by default. The @JsonPath expression allows customization of the value lookup, and even to define multiple JSON path expressions, to lookup values from multiple places until an expression returns an actual value.

要启用此功能,请在消息转换器上将 useProjectionForInterfaces 设置为 true。您还必须将 spring-data:spring-data-commonscom.jayway.jsonpath:json-path 添加到类路径。

To enable this feature, set the useProjectionForInterfaces to true on the message converter. You must also add spring-data:spring-data-commons and com.jayway.jsonpath:json-path to the class path.

当用作 @RabbitListener 方法的参数时,接口类型将如常自动传递给转换器。

When used as the parameter to a @RabbitListener method, the interface type is automatically passed to the converter as normal.

Converting From a Message With RabbitTemplate

如前所述,类型信息在消息标头中传递,以在从消息转换时帮助转换器。在大多数情况下,这工作正常。但是,当使用泛型类型时,它只能转换简单对象和已知的 “容器” 对象(列表、数组和映射)。从 2.0 版本开始,Jackson2JsonMessageConverter 实现 SmartMessageConverter,它允许与采用 ParameterizedTypeReference 参数的新 RabbitTemplate 方法一起使用。这允许转换复杂的泛型类型,如下面的示例所示:

As mentioned earlier, type information is conveyed in message headers to assist the converter when converting from a message. This works fine in most cases. However, when using generic types, it can only convert simple objects and known “container” objects (lists, arrays, and maps). Starting with version 2.0, the Jackson2JsonMessageConverter implements SmartMessageConverter, which lets it be used with the new RabbitTemplate methods that take a ParameterizedTypeReference argument. This allows conversion of complex generic types, as shown in the following example:

Thing1<Thing2<Cat, Hat>> thing1 =
    rabbitTemplate.receiveAndConvert(new ParameterizedTypeReference<Thing1<Thing2<Cat, Hat>>>() { });

从 2.1 版本开始,已删除 AbstractJsonMessageConverter 类。它不再是 Jackson2JsonMessageConverter 的基类。它已被 AbstractJackson2MessageConverter 取代。

Starting with version 2.1, the AbstractJsonMessageConverter class has been removed. It is no longer the base class for Jackson2JsonMessageConverter. It has been replaced by AbstractJackson2MessageConverter.

MarshallingMessageConverter

另一个选项是 MarshallingMessageConverter。它将委托给 Spring OXM 库的 MarshallerUnmarshaller 策略界面的实现。您可以在这里阅读有关该库的更多信息:https://docs.spring.io/spring/docs/current/spring-framework-reference/html/oxm.html。在配置方面,最常见的是只提供构造函数参数,因为大多数 Marshaller 的实现也实现了 Unmarshaller。以下示例演示如何配置 MarshallingMessageConverter

Yet another option is the MarshallingMessageConverter. It delegates to the Spring OXM library’s implementations of the Marshaller and Unmarshaller strategy interfaces. You can read more about that library here. In terms of configuration, it is most common to provide only the constructor argument, since most implementations of Marshaller also implement Unmarshaller. The following example shows how to configure a 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,反之亦然。

This class was introduced in version 2.1 and can be used to convert messages from and to XML.

Jackson2XmlMessageConverterJackson2JsonMessageConverter 都具有相同的基类:AbstractJackson2MessageConverter

Both Jackson2XmlMessageConverter and Jackson2JsonMessageConverter have the same base class: AbstractJackson2MessageConverter.

AbstractJackson2MessageConverter 类用于替换已删除的类:AbstractJsonMessageConverter

The AbstractJackson2MessageConverter class is introduced to replace a removed class: AbstractJsonMessageConverter.

Jackson2XmlMessageConverter 使用 com.fasterxml.jackson 2.x 库。

The Jackson2XmlMessageConverter uses the com.fasterxml.jackson 2.x library.

您可以以与 Jackson2JsonMessageConverter 相同的方式使用它,只是它支持 XML,而不是 JSON。以下示例配置 Jackson2JsonMessageConverter

You can use it the same way as Jackson2JsonMessageConverter, except it supports XML instead of JSON. The following example configures a 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

See Jackson2JsonMessageConverter for more information.

从 2.2 版本开始,如果不存在 contentType 属性或属性具有默认值 application/octet-stream,则假定为 application/xml。要恢复为之前的行为(返回未转换的 byte[]),请将转换器的 assumeSupportedContentType 属性设置为 false

Starting with version 2.2, application/xml is assumed if there is no contentType property, or it has the default value application/octet-stream. To revert to the previous behavior (return an unconverted byte[]), set the converter’s assumeSupportedContentType property to false.

ContentTypeDelegatingMessageConverter

此类在 1.4.2 版本中引入,允许根据 MessageProperties 中的 contentType 属性,委派给特定的 MessageConverter。默认情况下,如果不存在 contentType 属性或存在与任何已配置转换器都不匹配的值,则它委派给 SimpleMessageConverter。以下示例配置 ContentTypeDelegatingMessageConverter

This class was introduced in version 1.4.2 and allows delegation to a specific MessageConverter based on the content type property in the MessageProperties. By default, it delegates to a SimpleMessageConverter if there is no contentType property or there is a value that matches none of the configured converters. The following example configures a 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 Deserialization

本节介绍如何反序列化 Java 对象。

This section covers how to deserialize Java objects.

从不可信任的来源反序列化 Java 对象时,有一个可能的漏洞。

There is a possible vulnerability when deserializing java objects from untrusted sources.

如果接受来自不可信来源且 content-typeapplication/x-java-serialized-object 的消息,则应考虑配置允许反序列化的包和类。它适用于显式或通过配置使用 DefaultDeserializer 配置为使用 SimpleMessageConverterSerializerMessageConverter 的情况。

If you accept messages from untrusted sources with a content-type of application/x-java-serialized-object, you should consider configuring which packages and classes are allowed to be deserialized. This applies to both the SimpleMessageConverter and SerializerMessageConverter when it is configured to use a DefaultDeserializer either implicitly or via configuration.

默认情况下,允许列表为空,这意味着不会对任何类进行反序列化。

By default, the allowed list is empty, meaning no classes will be deserialized.

您可以设置模式列表,例如 thing1., thing1.thing2.Cat.MySafeClass

You can set a list of patterns, such as thing1., thing1.thing2.Cat or .MySafeClass.

模式按顺序检查,直到找到匹配项。如果没有匹配项,则会抛出 SecurityException

The patterns are checked in order until a match is found. If there is no match, a SecurityException is thrown.

您可以使用这些转换器上的 allowedListPatterns 属性设置模式。或者,如果您信任所有消息发起者,则可以将环境变量 SPRING_AMQP_DESERIALIZATION_TRUST_ALL 或系统属性 spring.amqp.deserialization.trust.all 设置为 true

You can set the patterns using the allowedListPatterns property on these converters. Alternatively, if you trust all message originators, you can set the environment variable SPRING_AMQP_DESERIALIZATION_TRUST_ALL or system property spring.amqp.deserialization.trust.all to true.

Message Properties Converters

MessagePropertiesConverter strategy 接口用于在 Rabbit 客户端 BasicProperties 和 Spring AMQP MessageProperties 之间进行转换。默认实现 (DefaultMessagePropertiesConverter) 通常足以满足大多数目的,但您可以在需要时实现自己的实现。默认属性转换器在大小不超过 1024 字节时将 BasicProperties 类型为 LongString 的元素转换为 String 实例。不会转换较大的 LongString 实例(请参见下一段)。此限制可以通过构造函数参数覆盖。

The MessagePropertiesConverter strategy interface is used to convert between the Rabbit Client BasicProperties and Spring AMQP MessageProperties. The default implementation (DefaultMessagePropertiesConverter) is usually sufficient for most purposes, but you can implement your own if needed. The default properties converter converts BasicProperties elements of type LongString to String instances when the size is not greater than 1024 bytes. Larger LongString instances are not converted (see the next paragraph). This limit can be overridden with a constructor argument.

从 1.6 版本开始,长度超过长字符串限制(默认:1024)的标头现在由 DefaultMessagePropertiesConverter 默认保留为 LongString 实例。您可以通过 getBytes[]toString()`或 `getStream() 方法访问内容。

Starting with version 1.6, headers longer than the long string limit (default: 1024) are now left as LongString instances by default by the DefaultMessagePropertiesConverter. You can access the contents through the getBytes[], toString(), or getStream() methods.

以前,DefaultMessagePropertiesConverter 将此类标头“转换”为 DataInputStream(实际上它只是引用了 LongString 实例的 DataInputStream)。在输出时,此标头没有被转换(除了转换为 String 之外,例如,toString() 调用流时为 java.io.DataInputStream@1d057a39)。

Previously, the DefaultMessagePropertiesConverter “converted” such headers to a DataInputStream (actually it just referenced the LongString instance’s DataInputStream). On output, this header was not converted (except to a String — for example, java.io.DataInputStream@1d057a39 by calling toString() on the stream).

现在,较大的传入 LongString 标头也在输出时正确“转换”(默认情况下)。

Large incoming LongString headers are now correctly “converted” on output, too (by default).

提供了一个新构造函数,可以让您将转换器配置为以前一样工作。以下清单显示了该方法的 Javadoc 注释和声明:

A new constructor is provided to let you configure the converter to work as before. The following listing shows the Javadoc comment and declaration of the method:

/**
 * 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 版本开始,MessageProperties 中添加了一个名为 correlationIdString 的新属性。以前,当使用 RabbitMQ 客户端使用的 BasicProperties 进行转换时,会执行不必要的 byte[] <→ String 转换,因为 MessageProperties.correlationIdbyte[],而 BasicProperties 使用 String。(最终,RabbitMQ 客户端使用 UTF-8 将 String 转换为字节以放入协议消息中。)

Also starting with version 1.6, a new property called correlationIdString has been added to MessageProperties. Previously, when converting to and from BasicProperties used by the RabbitMQ client, an unnecessary byte[] <→ String conversion was performed because MessageProperties.correlationId is a byte[], but BasicProperties uses a String. (Ultimately, the RabbitMQ client uses UTF-8 to convert the String to bytes to put in the protocol message).

为了提供最大的向后兼容性,已向 DefaultMessagePropertiesConverter 添加了一个名为 correlationIdPolicy 的新属性。这需要一个 DefaultMessagePropertiesConverter.CorrelationIdPolicy 枚举参数。默认情况下,它设置为 BYTES,这复制了以前的行为。

To provide maximum backwards compatibility, a new property called correlationIdPolicy has been added to the DefaultMessagePropertiesConverter. This takes a DefaultMessagePropertiesConverter.CorrelationIdPolicy enum argument. By default it is set to BYTES, which replicates the previous behavior.

对于入站消息:

For inbound messages:

  • STRING: Only the correlationIdString property is mapped

  • BYTES: Only the correlationId property is mapped

  • BOTH: Both properties are mapped

对于出站消息:

For outbound messages:

  • STRING: Only the correlationIdString property is mapped

  • BYTES: Only the correlationId property is mapped

  • BOTH: Both properties are considered, with the String property taking precedence

同样从版本 1.6 开始,入站 deliveryMode 属性不再映射到 MessageProperties.deliveryMode。它改为映射到 MessageProperties.receivedDeliveryMode。此外,入站 userId 属性不再映射到 MessageProperties.userId。它改为映射到 MessageProperties.receivedUserId。这些更改是为了避免在将相同的 MessageProperties 对象用于出站消息时意外传播这些属性。

Also starting with version 1.6, the inbound deliveryMode property is no longer mapped to MessageProperties.deliveryMode. It is mapped to MessageProperties.receivedDeliveryMode instead. Also, the inbound userId property is no longer mapped to MessageProperties.userId. It is mapped to MessageProperties.receivedUserId instead. These changes are to avoid unexpected propagation of these properties if the same MessageProperties object is used for an outbound message.

从版本 2.2 开始,DefaultMessagePropertiesConverter 将使用 getName() 而非 toString() 转换任何值类型为 Class<?> 的自定义标头;这避免了使用应用程序必须从 toString() 表示解析类名的消费。对于滚动升级,您可能需要更改使用者,以便在升级所有生产者之前理解这两种格式。

Starting with version 2.2, the DefaultMessagePropertiesConverter converts any custom headers with values of type Class<?> using getName() instead of toString(); this avoids consuming application having to parse the class name out of the toString() representation. For rolling upgrades, you may need to change your consumers to understand both formats until all producers are upgraded.