|
该版本仍在开发中,尚未被视为稳定。请使用最新的稳定版本,使用 Spring AMQP 4.0.0! |
消息转换器
这Amqp模板还定义了多种发送和接收消息的方法,这些方法委托给消息转换器.
这消息转换器为每个方向提供单一方法:一种转换为消息还有一个用于从一个消息.
注意,在转换为消息你还可以在对象之外提供属性。
这对象参数通常对应消息正体。
以下列表显示了消息转换器界面定义:
public interface MessageConverter {
Message toMessage(Object object, MessageProperties messageProperties)
throws MessageConversionException;
Object fromMessage(Message message) throws MessageConversionException;
}
相关内容消息- 发送方法Amqp模板比我们之前讨论的方法更简单,因为它们不需要消息实例。
相反,消息转换器负责“创造”每一个消息通过将提供的对象转换为消息然后加入任意提供的正体消息属性.
以下列表展示了各种方法的定义:
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;
在接收端,只有两种方法:一种接受队列名称,另一种依赖模板的“队列”属性已被设置。 以下列表展示了这两种方法的定义:
Object receiveAndConvert() throws AmqpException;
Object receiveAndConvert(String queueName) throws AmqpException;
这MessageListener适配器异步消费者中提到的也使用消息转换器. |
简易消息转换器
默认实现消息转换器策略称为简易消息转换器.
这是 实例所使用的转换器兔子模板如果你没有明确配置替代方案。
它处理基于文本的内容、序列化的 Java 对象和字节数组。
从消息
如果输入的内容类型消息以“文本”开头(例如,
“text/plain”),它还检查内容编码属性,以确定转换时所用字元集消息body字节数组到Java。字符串.
如果输入未设置内容编码属性消息,默认使用 UTF-8 字符集。
如果你需要覆盖该默认设置,可以配置一个实例简易消息转换器,设置其默认字符集并将其注入到兔子模板实例。
如果 content-type 属性的输入值消息设置为“application/x-java-serialized-object”,即简易消息转换器尝试将字节数组反序列化(重新水合)为 Java 对象。
虽然这对简单的原型制作可能有用,但我们不建议依赖 Java 序列化,因为这会导致生产者与消费者之间的紧密耦合。
当然,这也排除了双方使用非Java系统的可能。
由于AMQP是有线协议,若在此类限制下失去大部分优势将是遗憾的。
接下来的两节,我们将探讨一些传递丰富领域对象内容而不依赖 Java 序列化的替代方案。
对于所有其他内容类型,简易消息转换器返回消息正体内容直接以字节数组形式出现。
重要信息请参见 Java 反序列化。
串行化消息转换器
该转换器类似于简易消息转换器不过它可以与其他 Spring Framework 进行配置串行器和反串化器实现application/x-java-serialized-object转换。
重要信息请参见 Java 反序列化。
JacksonJsonMessageConverter
本节介绍使用JacksonJsonMessageConverter要将 转换为 和消息.
它包含以下部分:
这摘要Jackson2Message转换器,其实现及相关内容Jackson2JavaTypeMapperAPI已被弃用以移除4.0基于Jackson3的版本,支持各自的职业。
有关相关迁移指南,请参见已弃用类的JavaDocs。 |
转换为消息
如前文所述,通常不建议依赖Java序列化。
一种更灵活、更适合跨不同语言和平台的替代方案是JSON(JavaScript对象符号)。
转换器可以配置在任何设备上兔子模板实例以覆盖其对简易消息转换器违约。
这JacksonJsonMessageConverter使用 Jackson 3.x 库。
以下示例配置为JacksonJsonMessageConverter:
<bean class="org.springframework.amqp.rabbit.core.RabbitTemplate">
<property name="connectionFactory" ref="rabbitConnectionFactory"/>
<property name="messageConverter">
<bean class="org.springframework.amqp.support.converter.JacksonJsonMessageConverter">
<!-- if necessary, override the DefaultClassMapper -->
<property name="classMapper" ref="customClassMapper"/>
</bean>
</property>
</bean>
如上所示,JacksonJsonMessageConverter使用默认类映射器默认。
类型信息被添加到(并检索于 )消息属性.
如果入站消息不包含消息属性,但你知道预期类型,可以通过使用defaultType性质,如下例所示:
<bean id="jsonConverterWithDefaultType"
class="o.s.amqp.support.converter.JacksonJsonMessageConverter">
<property name="classMapper">
<bean class="org.springframework.amqp.support.converter.DefaultClassMapper">
<property name="defaultType" value="thing1.PurchaseOrder"/>
</bean>
</property>
</bean>
此外,您还可以根据TypeID页眉。
以下示例展示了如何实现:
@Bean
public JacksonJsonMessageConverter jsonMessageConverter() {
JacksonJsonMessageConverter jsonConverter = new JacksonJsonMessageConverter();
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;
}
现在,如果发送方系统将头部设置为东西1,转换器生成一个东西1物体,依此类推。
关于从非Spring应用转换消息的完整讨论,请参见“从非Spring应用接收JSON”示例应用。
从2.4.3版本开始,转换器不会添加内容编码如果支持媒体类型有字符集参数;这也用于编码。
一种新方法setSupportedMediaType已添加:
String utf16 = "application/json; charset=utf-16";
converter.setSupportedContentType(MimeTypeUtils.parseMimeType(utf16));
从一个消息
入站消息根据发送系统添加到头部的类型信息转换为对象。
从2.4.3版本开始,如果没有的话内容编码消息性质,转换器将尝试检测字符集参数内容类型消息属性并使用它。
如果两者都不存在,则支持媒体类型有字符集参数,用于解码,最终退回到默认字符集财产。
一种新方法setSupportedMediaType已添加:
String utf16 = "application/json; charset=utf-16";
converter.setSupportedContentType(MimeTypeUtils.parseMimeType(utf16));
在1.6之前的版本中,如果没有类型信息,转换将失败。 从版本 1.6 开始,如果类型信息缺失,转换器通过使用 Jackson 默认值(通常是映射)转换 JSON。
另外,从1.6版本开始,当你使用@RabbitListener在方法上,推断出的类型信息会被添加到消息属性.
这使得转换器能够转换为目标方法的参数类型。
这仅适用于存在一个无注释的参数或单个参数@Payload注解。
类型的参数消息在分析过程中被忽略。
默认情况下,推断出的类型信息会覆盖入站信息TypeID以及由发送系统创建的相关头部。
这使得接收系统能够自动转换为不同的域对象。
这仅适用于参数类型是具体的(非抽象的或接口的),或者参数类型来自java.util包。
在其他所有情况下,TypeID并且使用相关的头部。
在某些情况下,你可能希望覆盖默认行为,始终使用TypeID信息。
例如,假设你有@RabbitListener这需要一个东西1但消息包含怪物2是 的一个子类东西1(这很具体)。
推断出的类型是错误的。
处理这种情况时,设置类型优先权属性JacksonJsonMessageConverter自TYPE_ID而不是默认推断.
(该性质实际上存在于转换器的DefaultJacksonJavaTypeMapper但为方便起见,转换器上配备了设置器。)
如果你注入自定义类型映射器,应该在映射器上设置该属性。 |
从消息,一个来袭MessageProperties.getContentType()必须符合 JSON 标准(contentType.contains(“json”)用于检验)。
从2.2版本开始,application/json如果没有,则假设内容类型属性,或者它有默认值应用/八位元组流.
要恢复到之前的行为(返回未转换的字节[]),设置转换器assumeSupportedContentType属性到false.
如果不支持该内容类型,a警告日志消息无法转换带有内容类型的来信[...],是发射且message.getBody()返回为原样——作为字节[].
所以,要认识JacksonJsonMessageConverter消费者方面的需求,生产者必须添加内容类型消息属性——例如,作为application/json或text/x-json或者通过使用JacksonJsonMessageConverter,自动设置头部。
以下列表展示了若干变换器呼号: |
@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) {...}
在前述列表中的前四个例子中,转换器尝试转换为东西1类型。
第五个例子无效,因为我们无法确定哪个参数应接收消息有效载荷。
第六个例子中,Jackson默认值适用,因为通用类型为万用卡类型.
不过,你可以创建一个自定义转换器并使用targetMethod用 message 属性来决定将 JSON 转换为哪种类型。
这种类型推断只有在@RabbitListener注释在方法层面声明。
以阶级为基础@RabbitListener,转换后的类型用于选择@RabbitHandler调用方法。
因此,基础设施提供了目标对象消息属性,你可以在自定义转换器中使用它来确定类型。 |
从版本 1.6.11 开始,JacksonJsonMessageConverter因此,DefaultJacksonJavaTypeMapper (默认类映射器) 提供trustedPackages解决串行化小工具漏洞的选项。
默认情况下,为了向后兼容,JacksonJsonMessageConverter信任所有包——也就是说,它会使用该选项。* |
从2.4.7版本开始,转换器可以配置为返回Optional.empty()如果Jackson回来零在将消息正体反串行后,
这有助于@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(默认情况下),转换器会退回到原始消息主体(字节[]).
@Bean
JacksonJsonMessageConverter converter() {
JacksonJsonMessageConverter converter = new JacksonJsonMessageConverter();
converter.setNullAsOptionalEmpty(true);
return converter;
}
反序列化抽象类
在2.2.8版本之前,如果推断的类型@RabbitListener是一个抽象类(包括接口),转换器会退回查找头部中的类型信息,如果存在,则使用该信息;如果没有该条件,它会尝试创建抽象类。
这在自定义时带来了问题对象映射器使用自定义解串器处理抽象类的 Oracle,但收到的消息有无效的类型头部。
从2.2.8版本开始,之前的行为默认保留。如果你有这样的习俗对象映射器你想忽略类型头部,始终使用推断的类型进行转换,设置alwaysConvertToInferedType自true.
这是为实现向后兼容所必需的,并避免在尝试转换失败时产生的开销(使用标准)对象映射器).
使用 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 文档中的属性名称。
这@JsonPath表达式允许自定义值查找,甚至可以定义多个 JSON 路径表达式,从多个位置查找值,直到表达式返回实际值。
要启用此功能,请设置useProjectionForInterfaces自true在消息转换器上。
你还必须补充Spring-data:Spring-Data-commons和com.jayway.jsonpath:json-path去班级路径。
当用作参数时@RabbitListener该接口类型会像正常一样自动传递给转换器。
从消息跟兔子模板
如前所述,类型信息通过消息头部传递,以帮助转换器从消息转换。
大多数情况下这都没问题。
然而,使用泛型时,它只能转换简单对象和已知的“容器”对象(列表、数组和映射)。
从2.0版本开始,JacksonJsonMessageConverter实现智能消息转换器,使其可以与新的兔子模板采用参数化类型引用论点。
这允许转换复杂的泛型类型,如下示例所示:
Thing1<Thing2<Cat, Hat>> thing1 =
rabbitTemplate.receiveAndConvert(new ParameterizedTypeReference<Thing1<Thing2<Cat, Hat>>>() { });
MarshallingMessageConverter
还有一种选择是MarshallingMessageConverter.
它将 OXM 库的实现授权给 Spring OXM 库马歇勒和Unmarshaller策略界面。
你可以在这里了解更多关于那个图书馆的信息。
在配置方面,最常见的做法是只提供构造函数参数,因为大多数马歇勒同时实现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="someImplementationOfMarshallerAndUnmarshaller"/>
</bean>
</property>
</bean>
JacksonXmlMessage转换器
该类在2.1版本中引入,可用于将消息从XML转换到XML中。
双JacksonXmlMessage转换器和JacksonJsonMessageConverter具有相同的基类:摘要Jackson消息转换器.
这JacksonXmlMessage转换器使用 Jackson 3.x 库。
你可以用同样的方式来用它JacksonJsonMessageConverter,但支持XML而非JSON。
以下示例配置为JacksonJsonMessageConverter:
<bean id="xmlConverterWithDefaultType"
class="org.springframework.amqp.support.converter.JacksonXmlMessageConverter">
<property name="classMapper">
<bean class="org.springframework.amqp.support.converter.DefaultClassMapper">
<property name="defaultType" value="foo.PurchaseOrder"/>
</bean>
</property>
</bean>
更多信息请参见 JacksonJsonMessageConverter。
从2.2版本开始,application/xml如果没有,则假设内容类型属性,或者它有默认值应用/八位元组流.
要恢复到之前的行为(返回未转换的字节[]),设置转换器assumeSupportedContentType属性到false. |
内容类型委托消息转换器
该类于1.4.2版本引入,允许委派给特定对象消息转换器基于消息属性.
默认情况下,它委派给简易消息转换器如果没有内容类型或者存在一个与任何配置转换器都不匹配的值。
以下示例配置为内容类型委托消息转换器:
<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 对象时可能存在漏洞。 如果你接受来自不可信来源的消息,并且 默认情况下,允许列表为空,意味着不会有类被反序列化。 你可以设置一系列模式,比如 按顺序检查这些模式直到找到匹配。如果没有匹配,则 你可以用 |
消息属性转换器
这MessageProperties转换器策略接口用于在兔子客户端之间转换基本属性以及春季AMQP消息属性. 默认实现(默认消息属性转换器通常对大多数用途来说就足够了,但如果需要,你也可以自己实现。默认属性转换器会进行转换基本属性类型的元素长绳自字符串当 尺寸不大于1024字节。 较大长绳实例不被转换(见下一段)。该限制可以通过构造函数参数覆盖。
从1.6版本开始,超过长字符串限制(默认:1024)的头部现在保留为长绳实例默认由默认消息属性转换器. 您可以通过以下方式访问这些内容。getBytes[],toString()或getStream()方法。
此前,默认消息属性转换器将此类头部“转换”为DataInputStream(实际上它只是指的是长绳实例DataInputStream). 输出时,该头部不会被转换(除非转换为字符串——例如,java.io.DataInputStream@1d057a39通过呼叫toString()在直播中)。
大量进攻长绳现在,头部在输出时也会被正确“转换”(默认情况下)。
提供了一个新的构造函数,允许你配置转换器以恢复原有工作。以下列表展示了该方法的 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 开始,新增了一个名为关联IdString已被添加到消息属性. 之前,在转换到和从中转换时基本属性RabbitMQ 客户端使用的,是不必要的字节[] <→字符串改宗的原因是MessageProperties.correlationId是字节[]但基本属性使用字符串. (最终,RabbitMQ 客户端使用 UTF-8 来转换字符串转换为字节,用于协议消息中。
为了最大限度地向后兼容,新增了一个性质,称为correlationIdPolicy已被添加到默认消息属性转换器. 这需要一个DefaultMessagePropertiesConverter.CorrelationIdPolicyenum参数。默认情况下,它被设置为字节,重复了之前的行为。
对于入站消息:
-
字符串:只有关联IdString地产被映射 -
字节:只有关联Id地产被映射 -
双:两个性质都被映射
对于外发消息:
-
字符串:只有关联IdString地产被映射 -
字节:只有关联Id地产被映射 -
双:考虑了这两种性质,其中字符串财产优先权
同样从1.6版本开始,入站传递模式属性不再映射到MessageProperties.deliveryMode. 它被映射为MessageProperties.receivedDeliveryMode相反。 还有,进站用户ID属性不再映射到MessageProperties.userID. 它被映射为MessageProperties.receivedUserID相反。 这些变化是为了避免这些性质在相同情况下意外传播消息属性对象用于发送外发消息。
从2.2版本开始,默认消息属性转换器将任何带有 类型的值的自定义头转换为班级是<?>用getName()而不是toString(); 这样可以避免消耗应用程序,从中解析类名toString()表示法。 对于滚动升级,你可能需要更换消费者以理解两种格式,直到所有生产者都升级完成。