spring amqp Message Converters(8)

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对象用于出站消息,则这些更改将避免这些属性的意外传播。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值