javaspringspring-bootkotlin

How to define Jackson `ObjectMapper` bean based on Spring Boot's auto configuration?


I want to define an additional Jackson ObjectMapper bean that I'm going to use for just a part of my application. I want to base this bean on Spring Boot's auto configured ObjectMapper bean by calling .copy() on it.

To give you a basic idea of what I'm trying to do:

@Configuration
class WebConfig {
    @Bean
    fun customObjectMapper(jacksonObjectMapper: ObjectMapper): ObjectMapper = jacksonObjectMapper.copy()
        // and other customisations
}

But because Spring Boot's ObjectMapper bean is annotated with @ConditionalOnMissingBean, the various methods I've tried did not work. I've tried adding @DependsOn("jacksonObjectMapper"), I've tried adding @ConditionalOnBean.

Quoting from Spring Boot's source code:

@Bean
@Primary
@ConditionalOnMissingBean
ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
    return builder.createXmlMapper(false).build();
}

I could just depend my bean on Jackson2ObjectMapperBuilder, but I don't know how stable that method will be as I upgrade my Spring Boot version. I figured the option most resilient to upgrades is to depend directly on the ObjectMapper type.

How can I achieve this?


Solution

  • Jackson2ObjectMapperBuilder is stable, because the default ObjectMapper is directly built from it. It should be fine to use it to build your own.

    That said, given that the default ObjectMapper is annotated with both ConditionalOnMissingBean and Primary, something tells me it should also be possible to have multiple ObjectMapper beans in the spring context, otherwise, the Primary annotation doesn’t make any sense. You may want to assign your WebConfig a lower priority than the auto-configuration class that defines the spring default ObjectMapper, so, that it is instantiated before your config. The easiest way to debug the start up sequence is simply to put breakpoints in both classes and restart the app from your IDE. If that works, any ObjectMapper dependency without a qualifier will get the spring default, and yours can only be used with an explicit qualifier or by name.