此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring AMQP 3.2.6spring-doc.cadn.net.cn

消息转换器

AmqpTemplate还定义了几种用于发送和接收委托给MessageConverter. 这MessageConverter为每个方向提供一个方法:一个用于转换为Message另一个用于从Message. 请注意,当转换为Message,除了对象之外,您还可以提供属性。 这object参数通常对应于 Message 正文。 以下列表显示了MessageConverter接口定义:spring-doc.cadn.net.cn

public interface MessageConverter {

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

    Object fromMessage(Message message) throws MessageConversionException;

}

相关Message-sending 方法AmqpTemplate比我们之前讨论的方法更简单,因为它们不需要Message实例。 相反,该MessageConverter负责“创造”每个Message通过将提供的对象转换为Messagebody ,然后添加任何提供的MessageProperties. 以下列表显示了各种方法的定义:spring-doc.cadn.net.cn

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”属性已设置。 以下列表显示了这两种方法的定义:spring-doc.cadn.net.cn

Object receiveAndConvert() throws AmqpException;

Object receiveAndConvert(String queueName) throws AmqpException;
MessageListenerAdapter中提到的异步消费者也使用MessageConverter.

SimpleMessageConverter

默认实现的MessageConverter策略称为SimpleMessageConverter. 这是RabbitTemplate如果未显式配置备选方案。 它处理基于文本的内容、序列化的 Java 对象和字节数组。spring-doc.cadn.net.cn

Message

如果输入的内容类型Message以“text”开头(例如, “text/plain”),它还检查 content-encoding 属性以确定在转换Messagebody 字节数组转换为 JavaString. 如果未在输入上设置内容编码属性Message,它默认使用 UTF-8 字符集。 如果需要覆盖该默认设置,可以配置SimpleMessageConverter,设置其defaultCharset属性,并将其注入到RabbitTemplate实例。spring-doc.cadn.net.cn

如果输入的 content-type 属性值Message设置为“application/x-java-serialized-object”,则SimpleMessageConverter尝试将字节数组反序列化(再冻结)为 Java 对象。 虽然这对于简单的原型设计可能很有用,但我们不建议依赖 Java 序列化,因为它会导致生产者和消费者之间的紧密耦合。 当然,它也排除了在任何一方使用非 Java 系统的可能性。 由于 AMQP 是一种线级协议,因此由于此类限制而失去大部分优势将是不幸的。 在接下来的两节中,我们将探讨一些在不依赖 Java 序列化的情况下传递丰富领域对象内容的替代方法。spring-doc.cadn.net.cn

对于所有其他内容类型,SimpleMessageConverter返回Message正文内容直接作为字节数组。spring-doc.cadn.net.cn

有关重要信息,请参阅 Java 反序列化。spring-doc.cadn.net.cn

转换为Message

转换为Message从任意 Java 对象中,使用SimpleMessageConverter同样处理字节数组、字符串和可序列化实例。 它将每个字节转换为字节(在字节数组的情况下,没有任何内容需要转换),并相应地设置 content-type 属性。 如果Objectto be converted 与其中一种类型不匹配,则Messagebody 为 null。spring-doc.cadn.net.cn

SerializerMessageConverter

此转换器类似于SimpleMessageConverter除了它可以与其他 Spring Framework 一起配置SerializerDeserializer实现application/x-java-serialized-object转换。spring-doc.cadn.net.cn

有关重要信息,请参阅 Java 反序列化。spring-doc.cadn.net.cn

Jackson2JsonMessageConverter

本节介绍使用Jackson2JsonMessageConverterMessage. 它有以下部分:spring-doc.cadn.net.cn

转换为Message

如上一节所述,通常不建议依赖 Java 序列化。 JSON 是一种相当常见的替代方案,它更灵活、更易于跨不同语言和平台移植 (JavaScript 对象表示法)。 转换器可以配置在任何RabbitTemplate实例来覆盖其对SimpleMessageConverter违约。 这Jackson2JsonMessageConverter使用com.fasterxml.jackson2.x 库。 以下示例配置Jackson2JsonMessageConverter:spring-doc.cadn.net.cn

<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属性,如以下示例所示:spring-doc.cadn.net.cn

<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页眉。 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

@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对象,依此类推。 有关从非 Spring 应用程序转换消息的完整讨论,请参阅从非 Spring 应用程序接收 JSON 示例应用程序。spring-doc.cadn.net.cn

从 2.4.3 版开始,转换器将不会添加contentEncodingmessage 属性,如果supportedMediaType有一个charset参数;这也用于编码。 一种新方法setSupportedMediaType已添加:spring-doc.cadn.net.cn

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

Message

入站消息根据发送系统添加到标头的类型信息转换为对象。spring-doc.cadn.net.cn

从 2.4.3 版本开始,如果没有contentEncodingmessage 属性,转换器将尝试检测charset参数在contentTypemessage 属性并使用它。 如果两者都不存在,如果supportedMediaType有一个charset参数,它将用于解码,并最终回退到defaultCharset财产。 一种新方法setSupportedMediaType已添加:spring-doc.cadn.net.cn

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

在 1.6 之前的版本中,如果类型信息不存在,则转换将失败。 从 1.6 版开始,如果缺少类型信息,转换器将使用 Jackson 默认值(通常是映射)转换 JSON。spring-doc.cadn.net.cn

此外,从 1.6 版开始,当您使用@RabbitListener注释(在方法上),推断的类型信息将添加到MessageProperties. 这允许转换器转换为目标方法的参数类型。 仅当有一个参数没有注释或单个参数具有@Payload注解。 类型参数Message在分析过程中被忽略。spring-doc.cadn.net.cn

默认情况下,推断的类型信息将覆盖入站TypeId和创建的相关标头 由发送系统。 这允许接收系统自动转换为不同的域对象。 仅当参数类型是具体的(不是抽象或接口)或来自java.util包。 在所有其他情况下,TypeId和相关的标头。 在某些情况下,您可能希望覆盖默认行为并始终使用TypeId信息。 例如,假设您有一个@RabbitListener这需要一个Thing1参数,但消息包含Thing2那 是Thing1(这是具体的)。 推断的类型将不正确。 要处理这种情况,请将TypePrecedence属性Jackson2JsonMessageConverterTYPE_ID相反 的默认值INFERRED. (该属性实际上位于转换器的DefaultJackson2JavaTypeMapper,但转换器上提供了一个 setter 为方便起见。 如果注入自定义类型映射器,则应改为在映射器上设置属性。
Message,传入MessageProperties.getContentType()必须符合 JSON (contentType.contains("json")用于检查)。 从 2.2 版本开始,application/json如果没有contentType属性,或者它具有默认值application/octet-stream. 要恢复到以前的行为(返回未转换的byte[]),将转换器的assumeSupportedContentType属性设置为false. 如果不支持内容类型,则WARN日志消息Could not convert incoming message with content-type […​],发出,并且message.getBody()按原样返回 — 作为byte[]. 因此,为了满足Jackson2JsonMessageConverter要求,生产者必须添加contentTypemessage 属性 — 例如,作为application/jsontext/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类型。 第五个示例无效,因为我们无法确定哪个参数应该接收消息有效负载。在第六个示例中,Jackson 默认值适用,因为泛型类型是WildcardType.spring-doc.cadn.net.cn

但是,您可以创建一个自定义转换器并使用targetMethodmessage 属性来决定要转换的类型JSON 转换为。spring-doc.cadn.net.cn

只有当@RabbitListener注释是在方法级别声明的。使用类级@RabbitListener,转换后的类型用于选择哪个@RabbitHandler方法调用。因此,基础设施提供了targetObjectmessage 属性,您可以在自定义转换器中使用它来确定类型。
从 1.6.11 版本开始,Jackson2JsonMessageConverter因此,DefaultJackson2JavaTypeMapper (DefaultClassMapper) 提供trustedPackages克服序列化小工具漏洞的选项。 默认情况下,为了向后兼容,该Jackson2JsonMessageConverter信任所有包 — 也就是说,它用于选项。*

从 2.4.7 版开始,转换器可以配置为Optional.empty()如果Jackson回归null反序列化消息正文后。 这有利于@RabbitListeners 以两种方式接收空有效负载:spring-doc.cadn.net.cn

@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));
}

要启用此功能,请将setNullAsOptionalEmptytrue;什么时候false(默认值),转换器回退到原始消息正文 (byte[]).spring-doc.cadn.net.cn

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

反序列化抽象类

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

从 2.2.8 版开始,默认情况下保留以前的行为。如果你有这样的习俗ObjectMapper如果要忽略类型标头,并始终使用推断的类型进行转换,请将alwaysConvertToInferredTypetrue. 这是向后兼容性所必需的,并避免尝试转换失败时的开销(使用标准ObjectMapper).spring-doc.cadn.net.cn

使用 Spring 数据投影接口

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

interface SomeSample {

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

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

默认情况下,访问器方法将用于将属性名称查找为接收的 JSON 文档中的字段。 这@JsonPathexpression 允许自定义值查找,甚至可以定义多个 JSON 路径表达式,从多个位置查找值,直到表达式返回实际值。spring-doc.cadn.net.cn

要启用此功能,请将useProjectionForInterfacestrue在消息转换器上。 您还必须添加spring-data:spring-data-commonscom.jayway.jsonpath:json-path到类路径。spring-doc.cadn.net.cn

当用作参数时@RabbitListener方法,接口类型会正常自动传递给转换器。spring-doc.cadn.net.cn

MessageRabbitTemplate

如前所述,类型信息在消息头中传达,以帮助转换器从消息转换。 这在大多数情况下效果很好。 但是,当使用泛型类型时,它只能转换简单对象和已知的“容器”对象(列表、数组和映射)。 从 2.0 版开始,Jackson2JsonMessageConverter实现SmartMessageConverter,这允许它与新的RabbitTemplate采用ParameterizedTypeReference论点。 这允许转换复杂的泛型类型,如以下示例所示:spring-doc.cadn.net.cn

Thing1<Thing2<Cat, Hat>> thing1 =
    rabbitTemplate.receiveAndConvert(new ParameterizedTypeReference<Thing1<Thing2<Cat, Hat>>>() { });
从 2.1 版开始,AbstractJsonMessageConverter类已被删除。 它不再是Jackson2JsonMessageConverter. 它已被AbstractJackson2MessageConverter.

MarshallingMessageConverter

另一种选择是MarshallingMessageConverter. 它委托给 Spring OXM 库的MarshallerUnmarshaller策略接口。 您可以在此处阅读有关该库的更多信息。 在配置方面,最常见的是仅提供构造函数参数,因为大多数Marshaller还实现Unmarshaller. 以下示例演示如何配置MarshallingMessageConverter:spring-doc.cadn.net.cn

<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。spring-doc.cadn.net.cn

Jackson2XmlMessageConverterJackson2JsonMessageConverter具有相同的基类:AbstractJackson2MessageConverter.spring-doc.cadn.net.cn

AbstractJackson2MessageConverter引入 class 来替换已删除的类:AbstractJsonMessageConverter.

Jackson2XmlMessageConverter使用com.fasterxml.jackson2.x 库。spring-doc.cadn.net.cn

你可以用同样的方式使用它Jackson2JsonMessageConverter,只是它支持 XML 而不是 JSON。 以下示例配置Jackson2JsonMessageConverter:spring-doc.cadn.net.cn

<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>

有关详细信息,请参阅 Jackson2JsonMessageConverterspring-doc.cadn.net.cn

从 2.2 版本开始,application/xml如果没有contentType属性,或者它具有默认值application/octet-stream. 要恢复到以前的行为(返回未转换的byte[]),将转换器的assumeSupportedContentType属性设置为false.

ContentTypeDelegatingMessageConverter

此类是在版本 1.4.2 中引入的,允许委托给特定的MessageConverter基于MessageProperties. 默认情况下,它委托给SimpleMessageConverter如果没有contentType属性,或者存在与配置的任何转换器都不匹配的值。 以下示例配置ContentTypeDelegatingMessageConverter:spring-doc.cadn.net.cn

<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 对象。spring-doc.cadn.net.cn

从不受信任的来源反序列化 Java 对象时可能存在漏洞。spring-doc.cadn.net.cn

如果您接受来自不受信任来源的邮件,则使用content-typeapplication/x-java-serialized-object,你应该 考虑配置允许反序列化哪些包和类。 这适用于SimpleMessageConverterSerializerMessageConverter当它配置为使用DefaultDeserializer隐式或通过配置。spring-doc.cadn.net.cn

默认情况下,允许的列表为空,这意味着不会反序列化任何类。spring-doc.cadn.net.cn

您可以设置模式列表,例如thing1.,thing1.thing2.Cat.MySafeClass.spring-doc.cadn.net.cn

将按顺序检查模式,直到找到匹配项。 如果没有匹配项,则SecurityException被抛出。spring-doc.cadn.net.cn

您可以使用allowedListPatterns这些转换器的属性。 或者,如果您信任所有消息发起方,则可以将环境变量SPRING_AMQP_DESERIALIZATION_TRUST_ALL或系统属性spring.amqp.deserialization.trust.alltrue.spring-doc.cadn.net.cn

消息属性转换器

MessagePropertiesConverter策略接口用于在 Rabbit 客户端之间进行转换BasicProperties和 Spring AMQPMessageProperties. 默认实现 (DefaultMessagePropertiesConverter)通常足以满足大多数目的,但如果需要,您可以实现自己的。 默认属性转换器将BasicProperties类型元素LongStringString当大小不大于1024字节。 较大LongString实例不会转换(请参阅下一段)。 可以使用构造函数参数覆盖此限制。spring-doc.cadn.net.cn

从 1.6 版开始,长于长字符串限制(默认:1024)的标头现在保留为LongString实例默认情况下由DefaultMessagePropertiesConverter. 您可以通过getBytes[],toString()getStream()方法。spring-doc.cadn.net.cn

以前,DefaultMessagePropertiesConverter将此类标头“转换为DataInputStream(实际上它只是引用了LongString实例的DataInputStream). 在输出时,此标头未被转换(除了转换为 String — 例如java.io.DataInputStream@1d057a39通过调用toString()在直播中)。spring-doc.cadn.net.cn

大额进货LongString标头现在也会在输出时正确“转换”(默认)。spring-doc.cadn.net.cn

提供了一个新的构造函数,可让您将转换器配置为像以前一样工作。 以下列表显示了该方法的 Javadoc 注释和声明:spring-doc.cadn.net.cn

/**
 * 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. 以前,在与BasicPropertiesRabbitMQ 客户端使用的,不必要的byte[] <→ String执行转换是因为MessageProperties.correlationId是一个byte[]BasicProperties使用String. (最终,RabbitMQ 客户端使用 UTF-8 将String到字节以放入协议消息中)。spring-doc.cadn.net.cn

为了提供最大的向后兼容性,名为correlationIdPolicy已添加到DefaultMessagePropertiesConverter. 这需要一个DefaultMessagePropertiesConverter.CorrelationIdPolicyenum 参数。 默认情况下,它设置为BYTES,它复制了以前的行为。spring-doc.cadn.net.cn

对于入站邮件:spring-doc.cadn.net.cn

对于出站邮件:spring-doc.cadn.net.cn

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

从 2.2 版开始,DefaultMessagePropertiesConverter转换任何具有类型Class<?>getName()而不是toString();这避免了使用应用程序必须从toString()表示法。 对于滚动升级,您可能需要更改使用者以了解这两种格式,直到所有生产者都升级完毕。spring-doc.cadn.net.cn