为了更好地理解内容类型协商背后的机制和必要性,我们以以下消息处理程序为例,看一个非常简单的用例:Spring中文文档

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

前面示例中显示的处理程序需要将对象作为参数,并生成类型作为输出。 为了使框架成功地将传入作为参数传递给此处理程序,它必须以某种方式将类型的有效负载从 wire 格式转换为类型。 换言之,框架必须找到并应用适当的 . 为此,该框架需要用户提供一些指令。 其中一条指令已由处理程序方法本身的签名 ( type) 提供。 因此,从理论上讲,这应该(在某些情况下)足够了。 但是,对于大多数用例,为了选择合适的 ,框架需要额外的信息。 缺少的部分是.PersonStringMessageMessagePersonMessageConverterPersonMessageConvertercontentTypeSpring中文文档

Spring Cloud Stream 提供了三种定义机制(按优先级排序):contentTypeSpring中文文档

  1. HEADER:可以通过消息本身进行通信。通过提供标头,可以声明用于查找和应用相应 .contentTypecontentTypeMessageConverterSpring中文文档

  2. BINDING:可以通过设置属性来设置每个目标绑定。contentTypespring.cloud.stream.bindings.input.content-typeSpring中文文档

    属性名称中的段对应于目标的实际名称(在我们的例子中是“输入”)。此方法允许您在每个绑定的基础上声明用于查找和应用相应 .inputMessageConverter
  3. DEFAULT:如果标头或绑定中不存在,则默认内容类型用于 找到并应用相应的 .contentTypeMessageapplication/jsonMessageConverterSpring中文文档

如前所述,前面的列表还演示了平局情况下的优先顺序。例如,标头提供的内容类型优先于任何其他内容类型。 这同样适用于基于每个绑定设置的内容类型,这实质上允许您覆盖默认内容类型。 但是,它还提供了一个合理的默认值(这是根据社区反馈确定的)。Spring中文文档

设置默认值的另一个原因源于分布式微服务架构驱动的互操作性要求,其中生产者和消费者不仅可以在不同的 JVM 中运行,还可以在不同的非 JVM 平台上运行。application/jsonSpring中文文档

当 non-void 处理程序方法返回时,如果返回值已经是 ,则该值将成为有效负载。但是,当返回值不是 时,在继承时,将返回值作为有效负载来构造 来自输入的标头减去由 定义或过滤的标头。 默认情况下,那里只设置了一个标头:.这意味着新的没有标头集,从而确保可以发展。 您始终可以选择不从处理程序方法返回 a,您可以在其中注入所需的任何标头。MessageMessageMessageMessageMessageSpringIntegrationProperties.messageHandlerNotPropagatedHeaderscontentTypeMessagecontentTypecontentTypeMessageSpring中文文档

如果存在内部管道,则通过相同的转换过程将 发送到下一个处理程序。但是,如果没有内部管道,或者您已到达内部管道的末尾,则会将 发送回输出目标。MessageMessageSpring中文文档

为简单起见,我们假设这是应用程序中唯一的处理程序函数(我们假设没有内部管道)。
属性名称中的段对应于目标的实际名称(在我们的例子中是“输入”)。此方法允许您在每个绑定的基础上声明用于查找和应用相应 .inputMessageConverter

内容类型与参数类型

如前所述,要使框架选择适当的 ,它需要参数类型和内容类型信息(可选)。 选择适当参数的逻辑位于参数解析器 () 中,该解析器在调用用户定义的处理程序方法之前触发(当框架知道实际的参数类型时)。 如果参数类型与当前有效负载的类型不匹配,则框架将委托给 预先配置以查看其中任何一个是否可以转换有效载荷。 如您所见,MessageConverter 的操作是其参数之一。 该框架还确保提供的始终包含标头。 当不存在 contentType 标头时,它会注入每个绑定标头或默认标头。 参数类型的组合是框架确定消息是否可以转换为目标类型的机制。 如果未找到合适的异常,则会引发异常,您可以通过添加自定义来处理该异常(请参阅)。MessageConverterMessageConverterHandlerMethodArgumentResolversMessageConvertersObject fromMessage(Message<?> message, Class<?> targetClass);targetClassMessagecontentTypecontentTypecontentTypecontentTypeMessageConverterMessageConverterUser-defined Message ConvertersSpring中文文档

但是,如果有效负载类型与处理程序方法声明的目标类型匹配,该怎么办?在这种情况下,没有什么可转换的,并且 有效负载未修改地传递。虽然这听起来非常简单和合乎逻辑,但请记住将 or 作为参数的处理程序方法。 通过将目标类型声明为 (这是 Java 中的一切),您实际上放弃了转换过程。Message<?>ObjectObjectinstanceofSpring中文文档

不要指望仅基于 转换为其他类型。 请记住,这是对目标类型的补充。 如果您愿意,您可以提供提示,可能会考虑也可能不会考虑。MessagecontentTypecontentTypeMessageConverter
不要指望仅基于 转换为其他类型。 请记住,这是对目标类型的补充。 如果您愿意,您可以提供提示,可能会考虑也可能不会考虑。MessagecontentTypecontentTypeMessageConverter

消息转换器

MessageConverters定义两种方法:Spring中文文档

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

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

了解这些方法的契约及其用法非常重要,特别是在 Spring Cloud Stream 的上下文中。Spring中文文档

该方法将传入转换为参数类型。 的有效载荷可以是任何类型,它是 直到实际实现支持多种类型。 例如,某些 JSON 转换器可能支持有效负载类型为 、 和其他。 当应用程序包含内部管道(即输入→ handler1 → handler2 →时,这一点很重要。→输出)和上游处理程序的输出结果可能不是初始连线格式。fromMessageMessageMessageMessageConverterbyte[]StringMessageSpring中文文档

但是,该方法具有更严格的约定,并且必须始终转换为 wire 格式:。toMessageMessagebyte[]Spring中文文档

因此,出于所有意图和目的(尤其是在实现自己的转换器时),您将这两种方法视为具有以下签名:Spring中文文档

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

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