From f61ac29f3ffdcfea7535bd882ec0daefdd9057f9 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Thu, 18 Dec 2025 14:56:38 +0100 Subject: [PATCH] Document HttpMessageConverters detection changes This commit updates the reference documentation to better reflect changes applied in gh-48310. As of Spring Boot 4.0.1, `HttpMessageConverter` beans are all considered as "custom" converters and will be set ahead of the converters list in the auto-configuration support. For more specific changes, developers should contribute `ClientHttpMessageConvertersCustomizer` or `ServerHttpMessageConvertersCustomizer` beans to better reflect their intent: should the converter be configured for client, server or both? How should this converter be used? Previously, Spring Boot was relying on heuristics but they would cause issues for server-only converters, or for specific configuration that were not meant to replace the default converter for a given media type. Closes gh-48574 --- .../modules/reference/pages/web/servlet.adoc | 5 ++- .../MyHttpMessageConvertersConfiguration.java | 36 +++++++++++++++++++ .../MyHttpMessageConvertersConfiguration.kt | 25 +++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/web/servlet.adoc b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/web/servlet.adoc index d8c0fdacf4b..35f8e9a372e 100644 --- a/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/web/servlet.adoc +++ b/documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/web/servlet.adoc @@ -98,7 +98,10 @@ Any javadoc:org.springframework.http.converter.HttpMessageConverter[] bean that You can also override default converters in the same way. If you need to add or customize converters, you can declare one or more javadoc:org.springframework.boot.http.converter.autoconfigure.ClientHttpMessageConvertersCustomizer[] or -javadoc:org.springframework.boot.http.converter.autoconfigure.ServerHttpMessageConvertersCustomizer[] as beans, as shown in the following listing: +javadoc:org.springframework.boot.http.converter.autoconfigure.ServerHttpMessageConvertersCustomizer[] as beans. There, you can choose whether converter instances should be added +before default ones (`addCustomConverter`) or if they should override a specific default converter (like `withJsonConverter`). + +See the following listing for an example: include-code::MyHttpMessageConvertersConfiguration[] diff --git a/documentation/spring-boot-docs/src/main/java/org/springframework/boot/docs/web/servlet/springmvc/messageconverters/MyHttpMessageConvertersConfiguration.java b/documentation/spring-boot-docs/src/main/java/org/springframework/boot/docs/web/servlet/springmvc/messageconverters/MyHttpMessageConvertersConfiguration.java index 9e52a2919f6..5258733f87f 100644 --- a/documentation/spring-boot-docs/src/main/java/org/springframework/boot/docs/web/servlet/springmvc/messageconverters/MyHttpMessageConvertersConfiguration.java +++ b/documentation/spring-boot-docs/src/main/java/org/springframework/boot/docs/web/servlet/springmvc/messageconverters/MyHttpMessageConvertersConfiguration.java @@ -16,9 +16,17 @@ package org.springframework.boot.docs.web.servlet.springmvc.messageconverters; +import java.text.SimpleDateFormat; + +import tools.jackson.databind.json.JsonMapper; + import org.springframework.boot.http.converter.autoconfigure.ClientHttpMessageConvertersCustomizer; +import org.springframework.boot.http.converter.autoconfigure.ServerHttpMessageConvertersCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.HttpMessageConverters.ClientBuilder; +import org.springframework.http.converter.HttpMessageConverters.ServerBuilder; +import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter; @Configuration(proxyBeanMethods = false) public class MyHttpMessageConvertersConfiguration { @@ -29,4 +37,32 @@ public class MyHttpMessageConvertersConfiguration { .addCustomConverter(new AnotherHttpMessageConverter()); } + @Bean + public JacksonConverterCustomizer jacksonConverterCustomizer() { + JsonMapper jsonMapper = JsonMapper.builder().defaultDateFormat(new SimpleDateFormat("yyyy-MM")).build(); + return new JacksonConverterCustomizer(jsonMapper); + } + + // contribute a custom JSON converter to both client and server + static class JacksonConverterCustomizer + implements ClientHttpMessageConvertersCustomizer, ServerHttpMessageConvertersCustomizer { + + private final JsonMapper jsonMapper; + + JacksonConverterCustomizer(JsonMapper jsonMapper) { + this.jsonMapper = jsonMapper; + } + + @Override + public void customize(ClientBuilder builder) { + builder.withJsonConverter(new JacksonJsonHttpMessageConverter(this.jsonMapper)); + } + + @Override + public void customize(ServerBuilder builder) { + builder.withJsonConverter(new JacksonJsonHttpMessageConverter(this.jsonMapper)); + } + + } + } diff --git a/documentation/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/web/servlet/springmvc/messageconverters/MyHttpMessageConvertersConfiguration.kt b/documentation/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/web/servlet/springmvc/messageconverters/MyHttpMessageConvertersConfiguration.kt index f88e1917944..36dcb08897c 100644 --- a/documentation/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/web/servlet/springmvc/messageconverters/MyHttpMessageConvertersConfiguration.kt +++ b/documentation/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/web/servlet/springmvc/messageconverters/MyHttpMessageConvertersConfiguration.kt @@ -17,9 +17,13 @@ package org.springframework.boot.docs.web.servlet.springmvc.messageconverters import org.springframework.boot.http.converter.autoconfigure.ClientHttpMessageConvertersCustomizer +import org.springframework.boot.http.converter.autoconfigure.ServerHttpMessageConvertersCustomizer import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.http.converter.HttpMessageConverters +import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter +import tools.jackson.databind.json.JsonMapper +import java.text.SimpleDateFormat @Configuration(proxyBeanMethods = false) class MyHttpMessageConvertersConfiguration { @@ -33,5 +37,26 @@ class MyHttpMessageConvertersConfiguration { } } + @Bean + fun jacksonConverterCustomizer(): JacksonConverterCustomizer { + val jsonMapper = JsonMapper.builder() + .defaultDateFormat(SimpleDateFormat("yyyy-MM")) + .build() + return JacksonConverterCustomizer(jsonMapper) + } + + // contribute a custom JSON converter to both client and server + class JacksonConverterCustomizer(private val jsonMapper: JsonMapper) : + ClientHttpMessageConvertersCustomizer, ServerHttpMessageConvertersCustomizer { + + override fun customize(builder: HttpMessageConverters.ClientBuilder) { + builder.withJsonConverter(JacksonJsonHttpMessageConverter(this.jsonMapper)) + } + + override fun customize(builder: HttpMessageConverters.ServerBuilder) { + builder.withJsonConverter(JacksonJsonHttpMessageConverter(this.jsonMapper)) + } + } + }