为什么Spring Boot没有在休息控制器上使用@Primary Jackson ObjectMapper进行JSON序列化?

时间:2018-03-20 17:44:43

标签: spring-boot jackson2

我设置了一个类来返回自定义的ObjectMapper。据我所知,Spring Boot使用这个ObjectMapper的正确方法是将它声明为@Primary,它就是。

@Configuration
public class MyJacksonConfiguration {

    @Bean
    @Primary
    public ObjectMapper objectMapper() {
        return Jackson2ObjectMapperBuilder
            .json()
            .findModulesViaServiceLoader(true)
            .mixIn(Throwable.class, ThrowableMixin.class)
            .featuresToDisable(
                    WRITE_DATES_AS_TIMESTAMPS)
            .serializationInclusion(
                    Include.NON_ABSENT)
            .build();
    }
}

但是,当我从控制器方法返回一个对象时,它会使用默认的Jackson ObjectMapper配置进行序列化。

如果我向我的控制器添加一个显式的ObjectMapper并在其上调用writeValueAsString,我可以看到这个ObjectMapper是我希望Spring Boot使用的自定义的。

@RestController
public class TestController {

    @Autowired
    private TestService service;

    @Autowired
    private ObjectMapper mapper;

    @GetMapping(value = "/test", produces = "application/json")
    public TestResult getResult() {

        final TestResult ret = service.getResult();

        String test = "";
        try {
            test = mapper.writeValueAsString(ret);
            // test now contains the value I'd like returned by the controller!
        } catch (final JsonProcessingException e) {
            e.printStackTrace();
        }

        return ret;
    }
}

当我在我的控制器上运行测试时,测试类也使用自动装配的ObjectMapper。再次提供给测试的ObjectMapper是自定义的。

所以Spring在某种程度上了解了自定义的ObjectMapper,但我的其他控制器类并没有使用它。

我已尝试为Spring启用调试日志记录,但无法在日志中看到任何有用的内容。

知道可能会发生什么,或者我应该在哪里寻找追踪问题的其他地方?

编辑:似乎有多种方法可以做到这一点,但是我尝试这样做的方式似乎是一种推荐的方法,我想让它工作这个方式 - 见https://docs.spring.io/spring-boot/docs/1.4.7.RELEASE/reference/html/howto-spring-mvc.html#howto-customize-the-jackson-objectmapper的71.3 - 我在那里误解了什么吗?

4 个答案:

答案 0 :(得分:3)

Spring使用HttpMessageConverters呈现@ResponseBody(或来自@RestController的响应)。我认为你需要覆盖HttpMessageConverter。 您可以通过扩展WebMvcConfigurerAdapter并覆盖以下内容来实现。

 @Override
public void configureMessageConverters(
  List<HttpMessageConverter<?>> converters) {     
    messageConverters.add(new MappingJackson2HttpMessageConverter());
    super.configureMessageConverters(converters);
}

Spring documentation

答案 1 :(得分:1)

虽然其他答案显示了实现相同结果的替代方法,但这个问题的实际答案是我已经定义了一个扩展WebMvcConfigurationSupport的单独类。通过这样做,WebMvcAutoConfiguration bean已被禁用,因此Spring没有获取@Primary ObjectMapper。 (在WebMvcAutoConfiguration source中查找@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)。)

暂时删除延长WebMvcConfigurationSupport的班级,允许@Primary ObjectMapper按照Spring的预期被选中并使用。

由于我无法永久删除WebMvcConfigurationSupport扩展类,因此我在其中添加了以下内容:

@Autowired
private ObjectMapper mapper;

@Override
public void configureMessageConverters(final List<HttpMessageConverter<?>> converters) {
    converters.add(new MappingJackson2HttpMessageConverter(mapper));
    addDefaultHttpMessageConverters(converters);
    super.configureMessageConverters(converters);
}

答案 2 :(得分:0)

不会破坏字符串序列化(保持顺序)的正确方法:

public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    addDefaultHttpMessageConverters(converters);

    HttpMessageConverter<?> jacksonConverter =
        converters.stream()
            .filter(converter -> converter.getClass().equals(MappingJackson2HttpMessageConverter.class))
            .findFirst().orElseThrow(RuntimeException::new);

    converters.add(converters.indexOf(jacksonConverter), new MappingJackson2HttpMessageConverter(objectMapper));
}

答案 3 :(得分:-1)

因为你所做的是创建一个bean Autowired以供以后使用。 (就像你在代码中编写的那样)Spring启动使用自动配置的Jackson2ObjectMapperBuilder来获取用于序列化的默认ObjectMapper。

因此,如果要在其余控制器上自定义ObjectMapper序列化,则需要找到配置Jackson2ObjectMapperBuilder的方法。有两种方法可以做到这一点:

为spring声明自己的Jackson2ObjectMapperBuilder

@Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.serializationInclusion(JsonInclude.Include.NON_NULL);
    // other settings you want.
    return builder;
}

通过属性设置

您可以通过application.properties进行设置。例如,如果要启用漂亮打印,请设置spring.jackson.serialization.indent_output=true

有关详细信息,请在此处查看: Customize the Jackson ObjectMapper