是否可以在不求助于 XML 的情况下为 Spring 创建自定义 Jackson objectMapper?

Is it possible to create a custom Jackson objectMapper for Spring without resorting to XML?

It's easy to create a custom ObjectMapper 为 Spring,但配置需要 XML。我正在尝试减少 XML 配置的数量,这些东西确实不会改变,而无需重新部署我的整个系统。

所以标题说明了一切 - 我可以使用注释或其他一些 non-XML 方法来告诉 Spring、"Hey, please use my custom object mapper pls" 吗?


编辑:

这似乎不起作用

@Configuration
@EnableWebMvc
public class AppConfig {

    @Primary
    @Bean
    public ObjectMapper mapper(){
        ObjectMapper mapper = new ObjectMapper();
        mapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
        mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
        mapper.registerModule(new JodaModule());
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        return mapper;
    }
}

编辑 2: 我不相信 Spring 正在使用 my ObjectMapper。我有这个代码:

@Primary
@Bean
public ObjectMapper mapper(){
    ObjectMapper mapper = new ObjectMapper();
    JodaModule mod = new JodaModule();
    mod.addSerializer(DateTime.class, new JsonSerializer<DateTime>() {
        @Override
        public void serialize(DateTime dateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
            System.out.println("Hi, bob");
        }
    });
    mapper.registerModule(mod);

    mapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
    mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
    mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    mapper.enable(SerializationFeature.INDENT_OUTPUT);

    return mapper;
}

但是当我在 System.out.println("Hi, bob") 上设置断点时,它永远不会被调用 - 尽管我肯定是在序列化 DateTime。

您始终可以按照 Spring Docs 中提供的步骤进行操作。

If you want to replace the default ObjectMapper completely, define a @Bean of that type and mark it as @Primary.

Defining a @Bean of type Jackson2ObjectMapperBuilder will allow you to customize both default ObjectMapper and XmlMapper (used in MappingJackson2HttpMessageConverter and MappingJackson2XmlHttpMessageConverter respectively).

所以,要么你用你的 ObjectMapper 定义一个 @Bean 像这样:

@Primary
@Bean
public ObjectMapper mapper() {
    // Customize...
    return new ObjectMapper().setLocale(Locale.UK);
}

或者,您定义 Jackson2ObjectMapperBuilder 类型的 @Bean 并像这样自定义生成器:

@Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();

    // Customize
    builder.indentOutput(true).dateFormat(new SimpleDateFormat("yyyy-MM-dd"));
    return builder;
}

This blog entry 进一步描述自定义。

为了使用 @Bean 注释注册 bean,您必须按照 Spring docs about Java-based container configuration 中的描述在 @Configuration class 中声明 @Bean。 =32=]

这似乎是一个错误。 Spring 引导文档说用@Primary 注释 ObjectMapper Bean 应该使 Spring 上下文使用它而不是 Spring 的默认映射器。但是,这似乎不起作用。我找到了不使用 XML.

的解决方法
//Since Spring won't use the custom object mapper Bean defined below for
//HTTP message conversion(eg., when a Java object is returned from a controller,
//and should be converted to json using Jackson), we must override this method
//and tell it to use a custom message converter. We configure that custom converter
//below to use our customized Object mapper.
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    converters.add(mappingJackson2HttpMessageConverter());
}

//configures the converter to use our custom ObjectMapper
private MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
    MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
    //this line here
    jsonConverter.setObjectMapper(objectMapper());
    return jsonConverter;
}


//Primary annotation tells the Spring container to use this
//mapper as the primary mapper, instead of
//the Spring's defaultly configured mapper. Primary annotation
// DOESN'T work for some reason(this is most likely a bug and will be resolved in the future.
// When resolved, this Bean will be all it takes to tell Spring to use this ObjectMapper everywhere)
// That means that there won't be a need to configure the Http message converters manually(see method above).
@Primary
@Bean
public ObjectMapper objectMapper() {
    ObjectMapper mapper = new ObjectMapper();
    configureObjectMapper(mapper);
    return mapper;
}

//configure ObjectMapper any way you'd like
//This configuration tells the ObjectMapper to 
//(de)serialize all fields(private,protected,public,..) of all objects
//and to NOT (de)serialize any properties(getters,setters).
private void configureObjectMapper(ObjectMapper mapper) {
    //properties for jackson are fields with getters and setters
    //sets all properties to NOT be serialized or deserialized
    mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
    //tell the mapper to traverse all fields and not only default
    //default=public fields + fields with getters and setters
    //set all fields to be serialized and deserialized
    mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
}