对于最新的稳定版本,请使用 spring-cloud-stream 4.3.0spring-doc.cadn.net.cn

Mechanics

为了更好地理解内容类型协商背后的机制和必要性,我们以以下消息处理程序为例,来看看一个非常简单的用例:spring-doc.cadn.net.cn

public Function<Person, String> personFunction {..}
为简单起见,我们假设这是应用程序中唯一的处理程序函数(我们假设没有内部管道)。

前面示例中显示的处理程序需要一个Personobject 作为参数并生成一个String类型作为输出。 为了让框架成功通过传入的Message作为此处理程序的参数,它必须以某种方式转换Message类型从线格式转换为Person类型。 换句话说,框架必须找到并应用适当的MessageConverter. 为了实现这一点,框架需要用户的一些指令。 其中一条指令已由处理程序方法本身的签名(Person类型)。 因此,从理论上讲,这应该(并且在某些情况下)足够了。 但是,对于大多数用例,为了选择适当的MessageConverter,框架需要额外的信息。 缺失的那块是contentType.spring-doc.cadn.net.cn

Spring Cloud Stream 提供了三种机制来定义contentType(按优先顺序排列):spring-doc.cadn.net.cn

  1. HEADER:contentType可以通过消息本身进行交流。通过提供contentTypeheader 时,您可以声明要用于查找和应用相应内容类型MessageConverter.spring-doc.cadn.net.cn

  2. BINDING:contentType可以通过设置spring.cloud.stream.bindings.input.content-type财产。spring-doc.cadn.net.cn

    inputsegment 对应于目标的实际名称(在本例中为“输入”)。此方法允许你在每个绑定的基础上声明用于查找和应用相应内容类型的内容类型MessageConverter.
  3. 默认值:如果contentType不存在于Messageheader 或绑定,默认application/json内容类型用于 找到并应用适当的MessageConverter.spring-doc.cadn.net.cn

如前所述,前面的列表还演示了平局时的优先顺序。例如,标头提供的内容类型优先于任何其他内容类型。 这同样适用于按绑定设置的内容类型,这实质上允许您覆盖默认内容类型。 但是,它还提供了一个合理的默认值(这是根据社区反馈确定的)。spring-doc.cadn.net.cn

制作的另一个原因application/json默认源于分布式微服务架构驱动的互作性需求,其中生产者和消费者不仅在不同的 JVM 中运行,还可以在不同的非 JVM 平台上运行。spring-doc.cadn.net.cn

当非 void 处理程序方法返回时,如果返回值已经是MessageMessage成为有效载荷。但是,当返回值不是Message,新的Message在继承时使用返回值作为有效负载构造 来自输入的标头Message减去定义或过滤的标头SpringIntegrationProperties.messageHandlerNotPropagatedHeaders. 默认情况下,那里只设置了一个标头:contentType.这意味着新的Message没有contentTypeheader 设置,从而确保contentType可以进化。 您可以随时选择不返回Message从处理程序方法中,您可以在其中注入任何您想要的标头。spring-doc.cadn.net.cn

如果存在内部管道,则Message通过相同的转换过程发送到下一个处理程序。但是,如果没有内部管道或您已到达管道的末尾,则Message被发送回输出目标。spring-doc.cadn.net.cn

内容类型与参数类型

如前所述,对于框架选择合适的MessageConverter,它需要参数类型,以及(可选)内容类型信息。 选择适当MessageConverter驻留在参数解析器 (HandlerMethodArgumentResolvers),这在调用用户定义的处理程序方法之前触发(即框架已知实际参数类型时)。 如果参数类型与当前有效负载的类型不匹配,则框架会委托给 预配置MessageConverters看看其中任何一个是否可以转换有效负载。 如您所见,该Object fromMessage(Message<?> message, Class<?> targetClass);MessageConverter 的作需要targetClass作为其论点之一。 该框架还确保提供的Message始终包含一个contentType页眉。 当不存在 contentType 标头时,它会注入 per-bindingcontentTypeheader 或默认的contentType页眉。 的组合contentType参数类型是框架确定消息是否可以转换为目标类型的机制。 如果不合适MessageConverter,则会抛出异常,您可以通过添加自定义MessageConverter(参见User-defined Message Converters).spring-doc.cadn.net.cn

但是,如果有效负载类型与处理程序方法声明的目标类型匹配怎么办?在这种情况下,没有什么要转换的,并且 有效负载未修改地传递。虽然这听起来非常简单和合乎逻辑,但请记住,采用Message<?>Object作为论据。 通过将目标类型声明为Object(这是一个instanceofJava 中的所有内容),您基本上放弃了转换过程。spring-doc.cadn.net.cn

不期望Message仅基于contentType. 请记住,contentType与目标类型互补。 如果您愿意,可以提供提示,即MessageConverter可能会考虑也可能不会考虑。

消息转换器

MessageConverters定义两个方法:spring-doc.cadn.net.cn

Object fromMessage(Message<?> message, Class<?> targetClass);

Message<?> toMessage(Object payload, @Nullable MessageHeaders headers);

了解这些方法的契约及其用法非常重要,特别是在 Spring Cloud Stream 的上下文中。spring-doc.cadn.net.cn

fromMessage方法将传入的Message设置为参数类型。 的有效负载Message可以是任何类型,它是 直到实际实施MessageConverter以支持多种类型。 例如,某些 JSON 转换器可能支持有效负载类型为byte[],String,等。 当应用程序包含内部管道(即输入→ handler1 → handler2 →时,这一点很重要。→输出),上游处理程序的输出会产生Message可能不是初始线格式。spring-doc.cadn.net.cn

但是,toMessage方法具有更严格的契约,并且必须始终转换Message到线格式:byte[].spring-doc.cadn.net.cn

因此,出于所有意图和目的(尤其是在实现您自己的转换器时),您将这两种方法视为具有以下签名:spring-doc.cadn.net.cn

Object fromMessage(Message<?> message, Class<?> targetClass);

Message<byte[]> toMessage(Object payload, @Nullable MessageHeaders headers);