diff --git a/module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/GsonHttpMessageConvertersConfiguration.java b/module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/GsonHttpMessageConvertersConfiguration.java index 34f1e551e65..5c9c7cb522a 100644 --- a/module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/GsonHttpMessageConvertersConfiguration.java +++ b/module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/GsonHttpMessageConvertersConfiguration.java @@ -27,6 +27,7 @@ import org.springframework.boot.autoconfigure.condition.NoneNestedConditions; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; import org.springframework.http.converter.HttpMessageConverters.ClientBuilder; import org.springframework.http.converter.HttpMessageConverters.ServerBuilder; import org.springframework.http.converter.json.GsonHttpMessageConverter; @@ -48,6 +49,7 @@ class GsonHttpMessageConvertersConfiguration { static class GsonHttpMessageConverterConfiguration { @Bean + @Order(0) @ConditionalOnMissingBean(GsonHttpMessageConverter.class) GsonHttpConvertersCustomizer gsonHttpMessageConvertersCustomizer(Gson gson) { return new GsonHttpConvertersCustomizer(gson); diff --git a/module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/Jackson2HttpMessageConvertersConfiguration.java b/module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/Jackson2HttpMessageConvertersConfiguration.java index 151ef3237e1..c3ec3b21bcd 100644 --- a/module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/Jackson2HttpMessageConvertersConfiguration.java +++ b/module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/Jackson2HttpMessageConvertersConfiguration.java @@ -28,6 +28,7 @@ import org.springframework.boot.http.converter.autoconfigure.JacksonHttpMessageC import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; import org.springframework.http.converter.HttpMessageConverters.ClientBuilder; import org.springframework.http.converter.HttpMessageConverters.ServerBuilder; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; @@ -51,6 +52,7 @@ class Jackson2HttpMessageConvertersConfiguration { static class MappingJackson2HttpMessageConverterConfiguration { @Bean + @Order(0) @ConditionalOnMissingBean(org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.class) Jackson2JsonMessageConvertersCustomizer jackson2HttpMessageConvertersCustomizer(ObjectMapper objectMapper) { return new Jackson2JsonMessageConvertersCustomizer(objectMapper); @@ -64,6 +66,7 @@ class Jackson2HttpMessageConvertersConfiguration { protected static class MappingJackson2XmlHttpMessageConverterConfiguration { @Bean + @Order(0) @ConditionalOnMissingBean(org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter.class) Jackson2XmlMessageConvertersCustomizer mappingJackson2XmlHttpMessageConverter( Jackson2ObjectMapperBuilder builder) { diff --git a/module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/JacksonHttpMessageConvertersConfiguration.java b/module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/JacksonHttpMessageConvertersConfiguration.java index 9207b1986c1..25107d944a9 100644 --- a/module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/JacksonHttpMessageConvertersConfiguration.java +++ b/module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/JacksonHttpMessageConvertersConfiguration.java @@ -25,6 +25,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; import org.springframework.http.converter.HttpMessageConverters.ClientBuilder; import org.springframework.http.converter.HttpMessageConverters.ServerBuilder; import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter; @@ -47,6 +48,7 @@ class JacksonHttpMessageConvertersConfiguration { static class JacksonJsonHttpMessageConverterConfiguration { @Bean + @Order(0) @ConditionalOnMissingBean(value = JacksonJsonHttpMessageConverter.class, ignoredType = { "org.springframework.hateoas.server.mvc.TypeConstrainedJacksonJsonHttpMessageConverter", "org.springframework.data.rest.webmvc.alps.AlpsJacksonJsonHttpMessageConverter" }) @@ -62,6 +64,7 @@ class JacksonHttpMessageConvertersConfiguration { protected static class JacksonXmlHttpMessageConverterConfiguration { @Bean + @Order(0) @ConditionalOnMissingBean(JacksonXmlHttpMessageConverter.class) JacksonXmlHttpMessageConvertersCustomizer jacksonXmlHttpMessageConvertersCustomizer(XmlMapper xmlMapper) { return new JacksonXmlHttpMessageConvertersCustomizer(xmlMapper); diff --git a/module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/JsonbHttpMessageConvertersConfiguration.java b/module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/JsonbHttpMessageConvertersConfiguration.java index fa6a182e7f3..b246446ba3a 100644 --- a/module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/JsonbHttpMessageConvertersConfiguration.java +++ b/module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/JsonbHttpMessageConvertersConfiguration.java @@ -28,6 +28,7 @@ import org.springframework.boot.http.converter.autoconfigure.JacksonHttpMessageC import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; import org.springframework.http.converter.HttpMessageConverters.ClientBuilder; import org.springframework.http.converter.HttpMessageConverters.ServerBuilder; import org.springframework.http.converter.json.JsonbHttpMessageConverter; @@ -47,6 +48,7 @@ class JsonbHttpMessageConvertersConfiguration { static class JsonbHttpMessageConverterConfiguration { @Bean + @Order(0) @ConditionalOnMissingBean(JsonbHttpMessageConverter.class) JsonbHttpMessageConvertersCustomizer jsonbHttpMessageConvertersCustomizer(Jsonb jsonb) { return new JsonbHttpMessageConvertersCustomizer(jsonb); diff --git a/module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/KotlinSerializationHttpMessageConvertersConfiguration.java b/module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/KotlinSerializationHttpMessageConvertersConfiguration.java index 6129b0edbec..5c4ca821254 100644 --- a/module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/KotlinSerializationHttpMessageConvertersConfiguration.java +++ b/module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/KotlinSerializationHttpMessageConvertersConfiguration.java @@ -24,6 +24,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; import org.springframework.core.io.ResourceLoader; import org.springframework.http.converter.HttpMessageConverters.ClientBuilder; import org.springframework.http.converter.HttpMessageConverters.ServerBuilder; @@ -42,6 +43,7 @@ import org.springframework.util.ClassUtils; class KotlinSerializationHttpMessageConvertersConfiguration { @Bean + @Order(0) @ConditionalOnMissingBean(KotlinSerializationJsonHttpMessageConverter.class) KotlinSerializationJsonConvertersCustomizer kotlinSerializationJsonConvertersCustomizer(Json json, ResourceLoader resourceLoader) { diff --git a/module/spring-boot-http-converter/src/test/java/org/springframework/boot/http/converter/autoconfigure/HttpMessageConvertersAutoConfigurationTests.java b/module/spring-boot-http-converter/src/test/java/org/springframework/boot/http/converter/autoconfigure/HttpMessageConvertersAutoConfigurationTests.java index a6c9e258c50..a5a5adf5a74 100644 --- a/module/spring-boot-http-converter/src/test/java/org/springframework/boot/http/converter/autoconfigure/HttpMessageConvertersAutoConfigurationTests.java +++ b/module/spring-boot-http-converter/src/test/java/org/springframework/boot/http/converter/autoconfigure/HttpMessageConvertersAutoConfigurationTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.http.converter.autoconfigure; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -52,11 +53,16 @@ import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguratio import org.springframework.hateoas.RepresentationModel; import org.springframework.hateoas.mediatype.hal.forms.HalFormsHttpMessageConverter; import org.springframework.hateoas.server.mvc.TypeConstrainedJacksonJsonHttpMessageConverter; +import org.springframework.http.HttpInputMessage; +import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; +import org.springframework.http.converter.AbstractHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageConverters; import org.springframework.http.converter.HttpMessageConverters.ClientBuilder; import org.springframework.http.converter.HttpMessageConverters.ServerBuilder; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.json.GsonHttpMessageConverter; import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter; @@ -103,6 +109,14 @@ class HttpMessageConvertersAutoConfigurationTests { }); } + @Test + void jacksonServerCustomizer() { + this.contextRunner.withUserConfiguration(CustomJsonConverterConfig.class).run((context) -> { + assertConverterIsNotRegistered(context, JacksonJsonHttpMessageConverter.class); + assertConverterIsRegistered(context, CustomConverter.class); + }); + } + @Test void jacksonConverterWithBuilder() { this.contextRunner.withUserConfiguration(JacksonJsonMapperBuilderConfig.class).run((context) -> { @@ -455,16 +469,16 @@ class HttpMessageConvertersAutoConfigurationTests { private HttpMessageConverters getClientConverters(ApplicationContext context) { ClientBuilder clientBuilder = HttpMessageConverters.forClient().registerDefaults(); - context.getBeansOfType(ClientHttpMessageConvertersCustomizer.class) - .values() + context.getBeanProvider(ClientHttpMessageConvertersCustomizer.class) + .orderedStream() .forEach((customizer) -> customizer.customize(clientBuilder)); return clientBuilder.build(); } private HttpMessageConverters getServerConverters(ApplicationContext context) { ServerBuilder serverBuilder = HttpMessageConverters.forServer().registerDefaults(); - context.getBeansOfType(ServerHttpMessageConvertersCustomizer.class) - .values() + context.getBeanProvider(ServerHttpMessageConvertersCustomizer.class) + .orderedStream() .forEach((customizer) -> customizer.customize(serverBuilder)); return serverBuilder.build(); } @@ -503,6 +517,26 @@ class HttpMessageConvertersAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class CustomJsonConverterConfig { + + @Bean + JsonMapper jsonMapper() { + return new JsonMapper(); + } + + @Bean + ServerHttpMessageConvertersCustomizer jsonServerCustomizer() { + return (configurer) -> configurer.withJsonConverter(new CustomConverter(MediaType.APPLICATION_JSON)); + } + + @Bean + ClientHttpMessageConvertersCustomizer jsonClientCustomizer() { + return (configurer) -> configurer.withJsonConverter(new CustomConverter(MediaType.APPLICATION_JSON)); + } + + } + @Configuration(proxyBeanMethods = false) static class JacksonJsonMapperBuilderConfig { @@ -640,4 +674,30 @@ class HttpMessageConvertersAutoConfigurationTests { } + @SuppressWarnings("NullAway") + static class CustomConverter extends AbstractHttpMessageConverter { + + CustomConverter(MediaType supportedMediaType) { + super(supportedMediaType); + } + + @Override + protected boolean supports(Class clazz) { + return true; + } + + @Override + protected Object readInternal(Class clazz, HttpInputMessage inputMessage) + throws IOException, HttpMessageNotReadableException { + return null; + } + + @Override + protected void writeInternal(Object o, HttpOutputMessage outputMessage) + throws IOException, HttpMessageNotWritableException { + + } + + } + }