自定义对象映射器 bean 更改打开 Feign 客户端的默认属性/自动配置的 objectMapper bean

Posted

技术标签:

【中文标题】自定义对象映射器 bean 更改打开 Feign 客户端的默认属性/自动配置的 objectMapper bean【英文标题】:Custom object mapper bean changing the default properties of open Feign Client / auto configured objectMapper bean 【发布时间】:2020-07-21 00:37:04 【问题描述】:

我有 spring 的 open feign 客户端配置,如下所示:

public class AppFeignConfiguration

@Bean
public ErrorDecoder errorDecoder()

    return new FeignErrorDecoder();



@Bean
public Logger.Level logger()

    return Logger.Level.FULL;



@Bean
public Request.Options options()

    return new Request.Options( 30000, 30000 );


我在@FeignClient 中将其作为配置提供,如下所示

@FeignClient ( value = "apigateway-service", configuration = AppFeignConfiguration.class)

我相信 FeignClient 具有反序列化响应的默认配置(可能是具有某些属性的 ObjectMapper)。

到目前为止一切顺利。一切都按预期工作。

然后我创建了一个自定义对象映射器并将其作为 bean 返回,如下所示:

@Configuration
public class ObjectMapperConfig

  @Bean ( name = "plainObjectMapper")
  public ObjectMapper plainObjectMapper()
  
      return new ObjectMapper();
  

现在的问题是,这弄乱了FeignClient 的默认反序列化配置。

意思是,如果没有自定义 plainObjectMapper,它过去可以正常工作,没有任何 json 解析错误。但是在将自定义 plainObjectMapper 创建为 bean 之后,FeignClient 会抛出异常,说明一些未知属性或其他内容。

nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Unrecognized field "xyz" (class abc.def.ghi.class), not marked as ignorable; nested exception is com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException

我通过删除自定义 plainObjectMapper 确认了这一点,它和以前一样工作正常。

这似乎很奇怪!我给 bean 起了一个自定义名称,它不应该与 springboot 创建的 bean 混淆。

我该如何克服这种冲突?

【问题讨论】:

【参考方案1】:

这并不完全正确。

如果您查看FeignClientsConfiguration source,您可以看到它的编码器依赖于HttpMessageConverters 类。

那么,HttpMessageConvertersAutoConfiguration (source) 本身依赖于JacksonAutoConfiguration

仅当上下文中没有手动创建的对象映射器时,后者才会创建默认的 ObjectMapper bean(请参阅 @ConditionalOnMissingBean on the corresponding method) - 在这种情况下,不同的 bean 名称无关紧要。

所以,Feign 会隐含地依赖这个自动配置的ObjectMapper,但只有在你创建自己的之前。然后它开始使用你的。与许多其他春豆一样。

如果你真的需要拥有自己的ObjectMapper bean,那么你需要正确配置它。但是您可以通过配置属性自定义自动配置的 - 请参阅docs。

【讨论】:

但是@amseager,springboot自己创建了一个ObjectMapper的bean。这怎么不污染 FeignClient 的 ObjectMapper? 经过一番研究,我发现ObjectMapper 是由一个或另一个自动配置创建的,如果它不存在的话。如果我创建一个相同的文件,所有需要对象映射器的依赖项都将使用我创建的那个。整个应用程序中只有一个 ObjectMapper bean。当我们需要多个具有不同属性的 bean 时,这不会引起问题吗? 是的,在许多其他库(包括spring-cloud-openfeign)使用的常规 Spring Boot 应用程序中只有一个 ObjectMapper 自动配置 bean。由于@ConditionalOn... 注释,它不会在您手动执行时创建。这就是弹簧靴的魔力结束的地方。因此,作为最简单的解决方案,我不建议您尽可能创建自己的 ObjectMapper bean(或多个 bean),而是自定义默认值。 我明白了。但这不是缺陷吗?我的意思是为其他目的定制的 bean 正在污染其他一些autoconfigured bean 的属性。现在,如果我想继续使用自动配置和自定义 bean,我必须从自动配置中复制所有属性。 自定义默认 ObjectMapper 根本不是解决方案,因为这仍然会影响使用此 bean 的所有其他内容。这是春天的愚蠢,ObjectMappers 不应该这样分享。当您的应用程序使用 REST、Feign、RabbitMQ 等,并且还需要自定义 ObjectMapper 时,几乎不可能只自定义默认值以适应所有这些不同的需求。

以上是关于自定义对象映射器 bean 更改打开 Feign 客户端的默认属性/自动配置的 objectMapper bean的主要内容,如果未能解决你的问题,请参考以下文章

MapStruct使用说明

Spring 4 中 Websockets 的自定义对象映射器

如果映射器在中途失败并且 Hadoop 重试该映射器,自定义计数器会发生啥

双层设备映射器 - 自定义 dm-crypt

keycloak 从自定义协议映射器抛出身份验证错误

无法在 Sqoop 导出中自定义的映射器数量