diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java index e388f4c3e5..3bc23da13d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java @@ -307,7 +307,7 @@ public class JacksonAutoConfiguration { } private void configureModules(Jackson2ObjectMapperBuilder builder) { - builder.modulesToInstall(this.modules.toArray(new Module[0])); + builder.modulesToInstall((modules) -> modules.addAll(this.modules)); } private void configureLocale(Jackson2ObjectMapperBuilder builder) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfigurationTests.java index 29dde38a97..7ff485f6bd 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfigurationTests.java @@ -73,6 +73,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary; +import org.springframework.core.annotation.Order; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import static org.assertj.core.api.Assertions.assertThat; @@ -324,6 +325,17 @@ class JacksonAutoConfigurationTests { }); } + @Test + void customModulesRegisteredByBuilderCustomizerShouldBeRetained() { + this.contextRunner.withUserConfiguration(ModuleConfig.class, CustomModuleBuilderCustomizerConfig.class) + .run((context) -> { + ObjectMapper objectMapper = context.getBean(Jackson2ObjectMapperBuilder.class).build(); + assertThat(context.getBean(CustomModule.class).getOwners()).contains(objectMapper); + assertThat(objectMapper.getRegisteredModuleIds()).contains("module-A", "module-B", + CustomModule.class.getName()); + }); + } + @Test void defaultSerializationInclusion() { this.contextRunner.run((context) -> { @@ -592,6 +604,23 @@ class JacksonAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class CustomModuleBuilderCustomizerConfig { + + @Bean + @Order(-1) + Jackson2ObjectMapperBuilderCustomizer highPrecedenceCustomizer() { + return (builder) -> builder.modulesToInstall((modules) -> modules.add(new SimpleModule("module-A"))); + } + + @Bean + @Order(1) + Jackson2ObjectMapperBuilderCustomizer lowPrecedenceCustomizer() { + return (builder) -> builder.modulesToInstall((modules) -> modules.add(new SimpleModule("module-B"))); + } + + } + @Configuration(proxyBeanMethods = false) static class ObjectMapperBuilderConsumerConfig { diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/spring-mvc.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/spring-mvc.adoc index bdd2de2d89..fdeac720b7 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/spring-mvc.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/spring-mvc.adoc @@ -118,6 +118,8 @@ Such customizer beans can be ordered (Boot's own customizer has an order of 0), Any beans of type javadoc:com.fasterxml.jackson.databind.Module[] are automatically registered with the auto-configured javadoc:org.springframework.http.converter.json.Jackson2ObjectMapperBuilder[] and are applied to any javadoc:com.fasterxml.jackson.databind.ObjectMapper[] instances that it creates. This provides a global mechanism for contributing custom modules when you add new features to your application. +NOTE: If you wish to register additional modules programmatically using a javadoc:org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer[], make sure to use the `modulesToInstall` method that takes a consumer as the other variants are not additive. + If you want to replace the default javadoc:com.fasterxml.jackson.databind.ObjectMapper[] completely, either define a javadoc:org.springframework.context.annotation.Bean[format=annotation] of that type or, if you prefer the builder-based approach, define a javadoc:org.springframework.http.converter.json.Jackson2ObjectMapperBuilder[] javadoc:org.springframework.context.annotation.Bean[format=annotation]. When defining an javadoc:com.fasterxml.jackson.databind.ObjectMapper[] bean, marking it as javadoc:org.springframework.context.annotation.Primary[format=annotation] is recommended as the auto-configuration's javadoc:com.fasterxml.jackson.databind.ObjectMapper[] that it will replace is javadoc:org.springframework.context.annotation.Primary[format=annotation]. Note that, in either case, doing so disables all auto-configuration of the javadoc:com.fasterxml.jackson.databind.ObjectMapper[].