如何在Spring Cloud Netflix Feign中设置自定义的Jackson ObjectMapper?

22
我遇到了这样一种情况:我需要为一个第三方 API 定义一个单独的 @FeignClient。在这个客户端中,我想使用一个与我的 @Primary ObjectMapper 不同的自定义 Jackson ObjectMapper。我知道可以覆盖 Spring 的 Feign 配置默认值,但是我不清楚如何仅针对这个特定的客户端简单地覆盖 ObjectMapper。

你尝试过它但是不起作用吗?Spring Cloud Feign使用与Spring MVC相同的HttpMessageConverters对象。按照正常的Spring Boot方式进行配置应该可以“正常工作”(虽然我自己没有尝试过)。http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#howto-customize-the-jackson-objectmapper - spencergibb
@spencergibb 我可以覆盖ObjectMapper,并且所有Spring MVC控制器和所有Feign客户端都正确使用它。然而,我需要一个特定的Feign客户端(在众多客户端中)使用与默认配置的不同的对象映射器。我甚至不确定如何开始使其工作。 - Newbie
你需要使用之前发布的文档链接创建一个 SpringDecoder bean,并在那里进行一些处理。 - spencergibb
@spencergibb,我按照下面的答案工作了。感谢您的帮助。 - Newbie
https://blog.birost.com/a?ID=00600-16a0c674-d3f2-41a7-8e41-335e75f48dd0 - firstpostcommenter
5个回答

46
根据文档,您可以像下面展示的那样为Feign客户端提供自定义解码器。 Feign客户端接口:
@FeignClient(value = "foo", configuration = FooClientConfig.class)
public interface FooClient{
    //Your mappings
}

Feign客户端自定义配置:

@Configuration
public class FooClientConfig {

    @Bean
    public Decoder feignDecoder() {
        HttpMessageConverter jacksonConverter = new MappingJackson2HttpMessageConverter(customObjectMapper());

        HttpMessageConverters httpMessageConverters = new HttpMessageConverters(jacksonConverter);
        ObjectFactory<HttpMessageConverters> objectFactory = () -> httpMessageConverters;


        return new ResponseEntityDecoder(new SpringDecoder(objectFactory));
    }

    public ObjectMapper customObjectMapper(){
        ObjectMapper objectMapper = new ObjectMapper();
        //Customize as much as you want
        return objectMapper;
    }
}

10
对我而言,使用return new JacksonDecoder(customObjectMapper());非常简单。 - leveluptor
这个非常好用。谢谢。 - TuGordoBello
@TuGordoBello 请考虑此答案的更新版本。 - Newbie

14

跟随@NewBie的答案,我可以提供更好的方案...


  @Bean
  public Decoder feignDecoder() {
    return new JacksonDecoder();
  }

如果要在Feign客户端中使用Jackson消息转换器,请使用JacksonDecoder,因为SpringDecoder会增加生产环境下Feign客户端调用的平均延迟。
    <!-- feign-jackson decoder -->
    <dependency>
      <groupId>io.github.openfeign</groupId>
      <artifactId>feign-jackson</artifactId>
      <version>10.1.0</version>
    </dependency>

1
依赖是什么?版本是多少?你能展示一下这个pom.xml的条目吗? - rios0rios0
你能量化延迟增加的百分比、毫秒或其他指标吗?我想要一个参考点,了解这会带来多大的差异。看起来很干净。 - James Wynn
在我的印象中有了很大的改善,在生产环境中,每秒处理8k个请求时平均提高了10毫秒。 - suiwenfeng
你能展示如何在你的解决方案中传递自定义ObjectMapper吗? - ikhvjs

4
@NewBie的回答存在严重的性能问题。在new HttpMessageConverters的过程中,将执行loadclass操作,导致大量线程阻塞。如果您使用了此代码,请按以下方式进行修改:

ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(jacksonConverter);

改为

HttpMessageConverters httpMessageConverters = new HttpMessageConverters(jacksonConverter);
ObjectFactory<HttpMessageConverters> objectFactory = () -> httpMessageConverters;

您可以使用 JMeter 和 Arthas 来重现这个现象,并且修改过的程序已经得到了极大的改进。


0

如下定义一个自定义解码器,用@Configuration进行注释并将其设置为Feign客户端接口的参数,configuration = CustomFeignClientConfig.class

@Configuration
public class CustomFeignClientConfig {
    @Bean
    public Decoder feignDecoder() {
        return (response, type) -> {
            String bodyStr = Util.toString(response.body().asReader(Util.UTF_8));
            JavaType javaType = TypeFactory.defaultInstance().constructType(type);
            return new ObjectMapper().readValue( bodyStr, javaType);
        };
    }
}

0
你可以使用SpringDecoder和SpringEncoder。
public SpringDecoder(ObjectFactory<HttpMessageConverters> messageConverters)

已被弃用。您应该使用另一个构造函数:

public SpringDecoder(ObjectFactory<HttpMessageConverters> messageConverters,
            ObjectProvider<HttpMessageConverterCustomizer> customizers)

例如:

    @Bean
    public Decoder feignDecoder(ObjectProvider<HttpMessageConverterCustomizer> customizers) {
//        HttpMessageConverter<?> jacksonConverter = new MappingJackson2HttpMessageConverter(customObjectMapper());
//        HttpMessageConverters httpMessageConverters = new HttpMessageConverters(jacksonConverter);
        
        var httpMessageConverters = new HttpMessageConverters();
        return new ResponseEntityDecoder(new SpringDecoder(() -> httpMessageConverters, customizers));
    }

    @Bean
    public Encoder feignEncoder() {
        var httpMessageConverters = new HttpMessageConverters();
        return new SpringEncoder(() -> httpMessageConverters);
    }

如果您想编写一个解码时间的自定义程序,可以像这样编写:

@Component
public class HttpMessageCustomizer implements HttpMessageConverterCustomizer {
    @Override
    public void accept(List<HttpMessageConverter<?>> httpMessageConverters) {
        //customizing
    }

    @Override
    public Consumer<List<HttpMessageConverter<?>>> andThen(Consumer<? super List<HttpMessageConverter<?>>> after) {
        return HttpMessageConverterCustomizer.super.andThen(after);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接