spring-bootjackson

Add a property to a bean spring boot instead recreating it


I have an objectMapper bean set implicitly by spring boot, I'd like to add

this.objectMapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);

without:

@Bean
public ObjectMapper configure(){
    ObjectMapper om = new ObjectMapper();
    om.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);
}

Since it recreates the bean then configure it, I'd like just to make a little change on bean without recreating it from scratch messing implicity configurations made by spring.


Solution

  • Using spring.jackson.* properties

    Spring Boot already autoconfigures a Jackson ObjectMapper. You can add properties to the autoconfigured ObjectMapper by providing a spring.jackson.deserialization.* property. For example:

    spring.jackson.deserialization.read-unknown-enum-values-using-default-value=true
    

    Similarly, there are also spring.jackson.serialization.* and spring.jackson.mapper.* properties.

    Using Jackson2ObjectMapperBuilderCustomizer

    In addition, you can also declare a Jackson2ObjectMapperBuilderCustomizer in stead. For example:

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
        return builder -> builder.featuresToEnable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);
    }
    

    Using customizers in general

    If you're talking about how to add a property or how to customize any bean in general, then the pattern of using customizers is widely adopted in Spring Boot and you could do something similar.

    For example, let's say you're trying to configure a Foo bean and you have a builder like this:

    @Bean
    public Foo foo() {
        return Foo
            .builder()
            .foo("fizz")
            .bar("buzz")
            .build();
    }
    

    What you could in stead do is define your own FooCustomizer functional interface like this:

    interface FooCustomizer {
        Foo.Builder customize(Foo.Builder builder);
    }
    

    (note, if you're using Lombok to generate your builders, the builder class would be named Foo.FooBuilder by default in stead of Foo.Builder)

    And then you could use them like this:

    @Bean
    public FooCustomizer fizz() {
        return builder -> builder.foo("fizz");
    }
    
    @Bean
    public FooCustomizer buzz() {
        return builder -> builder.bar("buzz");
    }
    
    @Bean
    public Foo foo(List<FooCustomizer> customizers) {
        var builder = Foo.builder();
        customizers.forEach(customizer -> customizer.customize(builder));
        return builder.build();
    }
    

    Be aware, this example relies on the builders being mutable. If you're using immutable builders, you probably want to replace the forEach() at the end with a reduce() operator.