AmqpTemplate还定义了几种发送和接收消息的方法,这些消息委托给MessageConverter。MessageConverter为每个方向提供了一个方法:一个用于转换为消息,另一个用于从消息转化。注意,在转换为消息时,还可以提供对象之外的属性。对象参数通常对应于消息体。下面的清单显示了MessageConverter接口定义:
public interface MessageConverter {
Message toMessage(Object object, MessageProperties messageProperties)
throws MessageConversionException;
Object fromMessage(Message message) throws MessageConversionException;
}
AmqpTemplate上的相关消息发送方法比我们前面讨论的方法更简单,因为它们不需要消息实例。相反,MessageConverter负责“创建”每个消息,方法是将提供的对象转换为消息体的字节数组,然后添加任何提供的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。
SimpleMessageConverter
MessageConverter策略的默认实现称为SimpleMessageConverter。如果您没有显式地配置替代方案,则RabbitTemplate的实例将使用此转换器。它处理基于文本的内容、序列化的Java对象和字节数组。
Converting From a Message
如果输入消息的内容类型以“text”开头(例如,“text/plain”),它还检查content-encoding属性,以确定将消息体字节数组转换为Java字符串时使用的字符集。如果没有在输入消息上设置内容编码属性,则默认使用UTF-8字符集。如果需要覆盖默认设置,可以配置SimpleMessageConverter的一个实例,设置它的defaultCharset属性,并将其注入到一个RabbitTemplate实例中。
如果将输入消息的content-type属性值设置为“application/x-java-serialized-object”,SimpleMessageConverter将尝试将字节数组反序列化(重新水合物化)到Java对象中。虽然这对于简单的原型开发可能很有用,但是我们不建议依赖Java序列化,因为它会导致生产者和消费者之间的紧密耦合。当然,它还排除了任何一方使用非java系统的可能性。由于AMQP是一种线级协议,因此很不幸,由于这些限制而失去了这种优势。在接下来的两部分中,我们将探索一些替代方法,以便在不依赖Java序列化的情况下传递丰富的域对象内容。
对于所有其他内容类型,SimpleMessageConverter直接以字节数组的形式返回消息体内容。
有关重要信息,请参阅Java反序列化。
Converting To a Message
当从任意Java对象转换为消息时,SimpleMessageConverter同样处理字节数组、字符串和可序列化实例。它将每个元素转换为字节(对于字节数组,没有什么要转换的),并相应地处理content-type属性。如果要转换的对象不匹配其中一种类型,则消息体为null。
SerializerMessageConverter
这个转换器类似于SimpleMessageConverter,只是它可以配置其他Spring框架序列化器和反序列化器实现,用于application/x-java- serialized-object 转换。
有关重要信息,请参阅Java反序列化。
Jackson2JsonMessageConverter
本节介绍如何使用Jackson2JsonMessageConverter来转换消息。它包括以下各节:
•转换为消息
•从消息转换
Converting to a Message
如前一节所述,一般不建议依赖Java序列化。一个比较常见的替代方法是JSON (JavaScript对象表示法),它在不同的语言和平台上更加灵活和可移植。可以在任何RabbitTemplate实例上配置转换器,以覆盖它对SimpleMessageConverter缺省值的使用。Jackson2JsonMessageConverter使用com.fasterxml.jackson 2.x library.。下面的例子配置了一个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;
}
现在,如果发送系统将header设置为thing1,转换器将创建一个thing1对象,依此类推。有关从非spring应用程序转换消息的完整讨论,请参阅从非spring应用程序示例应用程序接收JSON。
Converting from a Message
进来的消息根据发送系统添加到header的类型信息转换为对象
在1.6之前的版本中,如果没有类型信息,转换将失败。从1.6版开始,如果缺少类型信息,转换器将使用Jackson默认值(通常是map)转换JSON。
同样,从1.6版开始,当您使用@RabbitListener注释(关于方法)时,推断出的类型信息将添加到MessageProperties中。这允许转换器转换为目标方法的参数类型。这只适用于没有注释的一个参数或@Payload注释的单个参数。在分析过程中忽略消息类型的参数。
默认情况下,推断的类型信息将覆盖发送系统创建的入站类型id和相关头。这允许接收系统自动转换为不同的域对象。这只适用于参数类型是具体的(而不是抽象的或接口)或来自java.util包的情况。在所有其他情况下,都使用TypeId和相关的头文件。在某些情况下,您可能希望覆盖默认行为并始终使用TypeId信息。例如,假设您有一个@RabbitListener,它接受Thing1参数,但是消息包含一个Thing2,它是Thing1的子类(具体的)。推断的类型将不正确。要处理这种情况,将Jackson2JsonMessageConverter上的TypePrecedence 属性设置为TYPE_ID,而不是推断的默认值。(该属性实际上位于转换器的DefaultJackson2JavaTypeMapper上,但是为了方便起见,转换器上提供了setter。)如果注入自定义类型映射器,则应在映射器上设置属性。
从消息转换时,传入的MessageProperties.getContentType()必须符合json (contentType.contains(“json”)用于检查)。否则,警告日志消息无法转换具有内容类型[…getbody()以字节[]的形式返回。因此,为了满足消费者端的Jackson2JsonMessageConverter需求,生产者必须添加contentType message属性——例如,作为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类型。第五个示例是无效的,因为我们无法确定哪个参数应该接收消息有效负载。在第6个示例中,由于泛型类型是通配符类型,所以使用Jackson缺省值。
但是,您可以创建一个自定义转换器,并使用targetMethod message属性来决定将JSON转换为哪种类型。
只有在方法级别声明@RabbitListener注释时才能实现这种类型推断。使用类级别@RabbitListener,转换后的类型用于选择要调用哪个@RabbitHandler方法。因此,基础设施提供targetObject消息属性,您可以在自定义转换器中使用该属性来确定类型。
从1.6.11版本开始,Jackson2JsonMessageConverter和DefaultJackson2JavaTypeMapper (DefaultClassMapper)提供trustedPackages选项来克服序列化gadget的漏洞。默认情况下,为了向后兼容,Jackson2JsonMessageConverter信任所有包——也就是说,它使用*作为选项。
Converting From a Message With RabbitTemplate
如前所述,类型信息在消息头中传递,以便在从消息转换时帮助转换器。这在大多数情况下都很有效。但是,当使用泛型类型时,它只能转换简单的对象和已知的“容器”对象(列表、数组和映射)。从2.0版开始,Jackson2JsonMessageConverter实现了SmartMessageConverter,它允许将其与接受ParameterizedTypeReference参数的新RabbitTemplate方法一起使用。这允许转换复杂的泛型类型,如下例所示:
Thing1<Thing2<Cat, Hat>> thing1 = rabbitTemplate.receiveAndConvert(new ParameterizedTypeReference<Thing1<Thing2
<Cat, Hat>>>() { });
从2.1版开始,AbstractJsonMessageConverter类已经被删除。它不再是Jackson2JsonMessageConverter的基类。它已经被AbstractJackson2MessageConverter所取代。、
MarshallingMessageConverter
443/5000
另一个选项是MarshallingMessageConverter。它委托给Spring OXM库对编组器和反编组器策略接口的实现。你可以在这里读到更多关于那个库的信息。就配置而言,只提供构造函数参数是最常见的,因为Marshaller的大多数实现也实现了反编组器。下面的例子展示了如何配置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。Jackson2XmlMessageConverter和Jackson2JsonMessageConverter都具有相同的基类:
AbstractJackson2MessageConverter。
引入AbstractJackson2MessageConverter类来替换已删除的类:AbstractJsonMessageConverter
Jackson2XmlMessageConverter使用com.fasterxml.jackson 2.x library
您可以像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>
ContentTypeDelegatingMessageConverter
这个类是在1.4.2版本中引入的,允许基于MessageProperties中的content type属性将委托给特定的MessageConverter。默认情况下,如果没有contentType属性或值与任何配置的转换器都不匹配,那么它将委托给SimpleMessageConverter。下面的示例配置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
This section covers how to deserialize Java objects.
从不可信源反序列化java对象时可能存在漏洞。
如果您使用application/x-java-serialized-object的内容类型接受来自不可信源的消息,您应该考虑配置哪些包和类允许反序列化。当SimpleMessageConverter被配置为隐式或通过配置使用DefaultDeserializer时,它同时适用于SimpleMessageConverter和SerializerMessageConverter。
默认情况下 白名单是空的,这意味着所有类都是反序列化的。
您可以设置模式列表,比如thing1。,thing1.thing2。猫或.MySafeClass。
在找到匹配之前,按顺序检查模式。如果没有匹配,则抛出SecurityException。
您可以在这些转换器上使用whiteListPatterns属性设置模式
Message Properties Converters
MessagePropertiesConverter策略接口用于在Rabbit客户机基本属性和Spring AMQP MessageProperties之间进行转换。默认实现(DefaultMessagePropertiesConverter)通常对于大多数用途都是足够的,但是如果需要,您可以实现自己的实现。当长度不大于1024字节时,默认的属性转换器将LongString类型的BasicProperties元素转换为String实例。不转换较大的LongString实例(参见下一段)。可以使用构造函数参数重写此限制。
从1.6版开始,DefaultMessagePropertiesConverter将比长字符串限制(默认值:1024)更长的头作为长字符串实例保留。您可以通过getBytes[]、toString()或getStream()方法访问内容。
之前,DefaultMessagePropertiesConverter将这些头“转换”为DataInputStream(实际上它只是引用了LongString实例的DataInputStream)。在输出时,这个头没有被转换(除了转换为字符串之外——例如java.io)。通过调用流上的toString(), DataInputStream@1d057a39。
输入的大长字符串头现在在输出时也正确地“转换”了(默认情况下)。
提供了一个新的构造函数,使您可以像以前一样配置转换器。以下
清单显示了方法的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中。以前,当转换到RabbitMQ客户机使用的基本属性和从基本属性转换时,会执行一个不必要的byte[] <→String转换,因为MessageProperties。correlationId是一个byte[],但是BasicProperties使用一个字符串。(最终,RabbitMQ客户机使用UTF-8将字符串转换为字节,以放入协议消息中)。
为了提供最大的向后兼容性,在DefaultMessagePropertiesConverter中添加了一个名为correlationIdPolicy的新属性。这将使用DefaultMessagePropertiesConverter。CorrelationIdPolicy enum的论点。默认情况下,它被设置为BYTES,这将复制前面的行为。
入站消息:
•BYTES:只有correlationId属性被映射
BOTH:两个属性都被映射
对于出站消息:
•STRING:只映射correlationIdString属性
•BYTES:只映射了correlationId属性
BOTH:考虑两个属性,字符串属性优先
同样,从1.6版开始,入站deliveryMode属性不再映射到MessageProperties.deliveryMode。它被映射到MessageProperties。receivedDeliveryMode代替。此外,入站userId属性不再映射到MessageProperties.userId。它被映射到MessageProperties。receivedUserId代替。如果将相同的MessageProperties对象用于出站消息,则这些更改将避免这些属性的意外传播。