这个版本仍在开发中,尚未达到稳定状态。要使用最新稳定版,请使用 spring-cloud-stream 5.0.1 spring-doc.cadn.net.cn

机械学

为了更好地理解内容协商机制及其必要性,我们通过一个非常简单的用例来说明,使用以下消息处理器作为示例:spring-doc.cadn.net.cn

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

前面示例中显示的处理器期望将Person对象作为参数,并生成String类型的输出。为了使框架能够成功地将传入的Message作为参数传递给此处理器,它必须以某种方式将来自网络格式的Message类型的有效负载转换为Person类型。换句话说,框架必须定位并应用适当的MessageConverter。为了实现这一点,框架需要用户的一些指示。其中一条指令已经由处理器方法本身的签名提供(Person类型)。因此,在理论上,这应该足够了(在某些情况下也是如此)。然而,对于大多数用例,为了选择合适的MessageConverter,框架还需要额外的信息。缺失的部分是contentTypespring-doc.cadn.net.cn

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

  1. HEADER: contentType 可通过消息本身进行通信。提供一个 contentType 头,声明要使用的内容类型以定位和应用适当的 MessageConverterspring-doc.cadn.net.cn

  2. 绑定:可以通过设置spring.cloud.stream.bindings.input.content-type属性来为每个目标绑定设置contentTypespring-doc.cadn.net.cn

    属性名称中的 input 段对应于目标的实际名称(在我们的例子中是“input”)。这种方法允许您根据每个绑定声明要使用的数据类型,以定位并应用适当的 MessageConverter
  3. 默认值:如果 contentType 不在Message 头或绑定中,则使用默认的application/json内容类型来定位并应用适当的MessageConverterspring-doc.cadn.net.cn

如前所述,前面的列表还演示了在出现平局时的优先级顺序。例如,由报头提供的内容类型优先于任何其他内容类型。<br/>对于按绑定设置的内容类型也是如此,这实际上让您能够覆盖默认的内容类型。<br/>然而,它也提供了一个合理的默认值(这是根据社区反馈确定的)。spring-doc.cadn.net.cn

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

当非 void 处理程序方法返回时,如果返回值已经是 Message,那么该Message将成为有效载荷。然而,当返回值不是 Message 时,将以返回值作为有效载荷并继承输入Message减去由SpringIntegrationProperties.messageHandlerNotPropagatedHeaders定义或过滤的标头来构建新的Message
默认情况下,只有一个标头被设置:contentType。这意味着新Message没有设置contentType标头,从而确保contentType可以发展。
您始终可以选择不从处理程序方法返回Message,并在其中注入任何标头。spring-doc.cadn.net.cn

如果有内部管道,则通过相同的转换过程将Message发送到下一个处理器。但是,如果没有内部管道或者已经到达了末端,则将Message发送回输出目标。spring-doc.cadn.net.cn

内容类型与参数类型

如前所述,为了使框架选择适当的MessageConverter,它需要参数类型和(可选地)内容类型信息。选择合适的 MessageConverter 的逻辑由参数解析器(HandlerMethodArgumentResolvers)负责,在调用用户定义的处理器方法之前触发(此时框架已知实际参数类型)。如果参数类型与当前有效负载的类型不匹配,框架将委托给预先配置的MessageConverters堆栈,查看它们中的任何一个是否可以转换有效负载。如您所见,Object fromMessage(Message<?> message, Class<?> targetClass); 消息转换器的操作将其一个参数设为 targetClass。框架还确保提供的 Message 始终包含一个 contentType 头部。当没有现成的contentType头时,它会注入每个绑定的contentType头或默认的contentType头。参数类型的组合contentType是框架确定消息是否可以转换为目标类型的方法。如果未找到合适的 MessageConverter,则抛出异常,您可以通过添加自定义 MessageConverter 来处理该异常(参见 User-defined Message Converters)。spring-doc.cadn.net.cn

但是,如果有效负载类型与处理程序方法声明的目标类型相匹配呢?在这种情况下,不需要进行转换,有效负载将未经修改地传递。虽然这听起来相当直接且合乎逻辑,请记住那些采用Message<?>Object作为参数的处理程序方法。
通过将目标类型声明为Object(即Java中的instanceof),您实际上放弃了转换过程。spring-doc.cadn.net.cn

不要期望仅基于contentTypeMessage转换为其他类型。

spring-doc.cadn.net.cn

请记住,contentType与目标类型互补。spring-doc.cadn.net.cn

如果您愿意,可以提供一个提示,MessageConverter可能会考虑也可能不会考虑该提示。spring-doc.cadn.net.cn

消息转换器

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等有效负载类型。
当应用程序包含一个内部管道(即输入 → 处理程序1 → 处理程序2 →...→ 输出)时,这一点非常重要。上游处理程序的输出可能会产生一个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);