Skip to content

Commit 9d1db68

Browse files
bclozelsnicoll
authored andcommitted
Fix message converter customizers order
Prior to this commit, spring-projectsgh-48310 separated client and server message converter configurations by switching from message converter instances as beans in the application context, to server/client customizers that are applied to the `HttpMessageConverters` instances while being built. This change did not order the new ClientHttpMessageConvertersCustomizer or ServerHttpMessageConvertersCustomizer, letting those being at the "lowest precedence" default. As customizers, this means they are applied last and custom instances cannot take over. This commit ensures that such customizers provided by Spring Boot are now ordered at "0" to let applications ones take over. Fixes spring-projectsgh-48635
1 parent 5d1d517 commit 9d1db68

6 files changed

Lines changed: 76 additions & 4 deletions

File tree

module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/GsonHttpMessageConvertersConfiguration.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.springframework.context.annotation.Bean;
2828
import org.springframework.context.annotation.Conditional;
2929
import org.springframework.context.annotation.Configuration;
30+
import org.springframework.core.annotation.Order;
3031
import org.springframework.http.converter.HttpMessageConverters.ClientBuilder;
3132
import org.springframework.http.converter.HttpMessageConverters.ServerBuilder;
3233
import org.springframework.http.converter.json.GsonHttpMessageConverter;
@@ -48,6 +49,7 @@ class GsonHttpMessageConvertersConfiguration {
4849
static class GsonHttpMessageConverterConfiguration {
4950

5051
@Bean
52+
@Order(0)
5153
@ConditionalOnMissingBean(GsonHttpMessageConverter.class)
5254
GsonHttpConvertersCustomizer gsonHttpMessageConvertersCustomizer(Gson gson) {
5355
return new GsonHttpConvertersCustomizer(gson);

module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/Jackson2HttpMessageConvertersConfiguration.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.context.annotation.Bean;
2929
import org.springframework.context.annotation.Conditional;
3030
import org.springframework.context.annotation.Configuration;
31+
import org.springframework.core.annotation.Order;
3132
import org.springframework.http.converter.HttpMessageConverters.ClientBuilder;
3233
import org.springframework.http.converter.HttpMessageConverters.ServerBuilder;
3334
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
@@ -51,6 +52,7 @@ class Jackson2HttpMessageConvertersConfiguration {
5152
static class MappingJackson2HttpMessageConverterConfiguration {
5253

5354
@Bean
55+
@Order(0)
5456
@ConditionalOnMissingBean(org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.class)
5557
Jackson2JsonMessageConvertersCustomizer jackson2HttpMessageConvertersCustomizer(ObjectMapper objectMapper) {
5658
return new Jackson2JsonMessageConvertersCustomizer(objectMapper);
@@ -64,6 +66,7 @@ Jackson2JsonMessageConvertersCustomizer jackson2HttpMessageConvertersCustomizer(
6466
protected static class MappingJackson2XmlHttpMessageConverterConfiguration {
6567

6668
@Bean
69+
@Order(0)
6770
@ConditionalOnMissingBean(org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter.class)
6871
Jackson2XmlMessageConvertersCustomizer mappingJackson2XmlHttpMessageConverter(
6972
Jackson2ObjectMapperBuilder builder) {

module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/JacksonHttpMessageConvertersConfiguration.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
2626
import org.springframework.context.annotation.Bean;
2727
import org.springframework.context.annotation.Configuration;
28+
import org.springframework.core.annotation.Order;
2829
import org.springframework.http.converter.HttpMessageConverters.ClientBuilder;
2930
import org.springframework.http.converter.HttpMessageConverters.ServerBuilder;
3031
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
@@ -47,6 +48,7 @@ class JacksonHttpMessageConvertersConfiguration {
4748
static class JacksonJsonHttpMessageConverterConfiguration {
4849

4950
@Bean
51+
@Order(0)
5052
@ConditionalOnMissingBean(value = JacksonJsonHttpMessageConverter.class,
5153
ignoredType = { "org.springframework.hateoas.server.mvc.TypeConstrainedJacksonJsonHttpMessageConverter",
5254
"org.springframework.data.rest.webmvc.alps.AlpsJacksonJsonHttpMessageConverter" })
@@ -62,6 +64,7 @@ JacksonJsonHttpMessageConvertersCustomizer jacksonJsonHttpMessageConvertersCusto
6264
protected static class JacksonXmlHttpMessageConverterConfiguration {
6365

6466
@Bean
67+
@Order(0)
6568
@ConditionalOnMissingBean(JacksonXmlHttpMessageConverter.class)
6669
JacksonXmlHttpMessageConvertersCustomizer jacksonXmlHttpMessageConvertersCustomizer(XmlMapper xmlMapper) {
6770
return new JacksonXmlHttpMessageConvertersCustomizer(xmlMapper);

module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/JsonbHttpMessageConvertersConfiguration.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.context.annotation.Bean;
2929
import org.springframework.context.annotation.Conditional;
3030
import org.springframework.context.annotation.Configuration;
31+
import org.springframework.core.annotation.Order;
3132
import org.springframework.http.converter.HttpMessageConverters.ClientBuilder;
3233
import org.springframework.http.converter.HttpMessageConverters.ServerBuilder;
3334
import org.springframework.http.converter.json.JsonbHttpMessageConverter;
@@ -47,6 +48,7 @@ class JsonbHttpMessageConvertersConfiguration {
4748
static class JsonbHttpMessageConverterConfiguration {
4849

4950
@Bean
51+
@Order(0)
5052
@ConditionalOnMissingBean(JsonbHttpMessageConverter.class)
5153
JsonbHttpMessageConvertersCustomizer jsonbHttpMessageConvertersCustomizer(Jsonb jsonb) {
5254
return new JsonbHttpMessageConvertersCustomizer(jsonb);

module/spring-boot-http-converter/src/main/java/org/springframework/boot/http/converter/autoconfigure/KotlinSerializationHttpMessageConvertersConfiguration.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2525
import org.springframework.context.annotation.Bean;
2626
import org.springframework.context.annotation.Configuration;
27+
import org.springframework.core.annotation.Order;
2728
import org.springframework.core.io.ResourceLoader;
2829
import org.springframework.http.converter.HttpMessageConverters.ClientBuilder;
2930
import org.springframework.http.converter.HttpMessageConverters.ServerBuilder;
@@ -42,6 +43,7 @@
4243
class KotlinSerializationHttpMessageConvertersConfiguration {
4344

4445
@Bean
46+
@Order(0)
4547
@ConditionalOnMissingBean(KotlinSerializationJsonHttpMessageConverter.class)
4648
KotlinSerializationJsonConvertersCustomizer kotlinSerializationJsonConvertersCustomizer(Json json,
4749
ResourceLoader resourceLoader) {

module/spring-boot-http-converter/src/test/java/org/springframework/boot/http/converter/autoconfigure/HttpMessageConvertersAutoConfigurationTests.java

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.boot.http.converter.autoconfigure;
1818

19+
import java.io.IOException;
1920
import java.nio.charset.StandardCharsets;
2021
import java.util.ArrayList;
2122
import java.util.List;
@@ -52,11 +53,16 @@
5253
import org.springframework.hateoas.RepresentationModel;
5354
import org.springframework.hateoas.mediatype.hal.forms.HalFormsHttpMessageConverter;
5455
import org.springframework.hateoas.server.mvc.TypeConstrainedJacksonJsonHttpMessageConverter;
56+
import org.springframework.http.HttpInputMessage;
57+
import org.springframework.http.HttpOutputMessage;
5558
import org.springframework.http.MediaType;
59+
import org.springframework.http.converter.AbstractHttpMessageConverter;
5660
import org.springframework.http.converter.HttpMessageConverter;
5761
import org.springframework.http.converter.HttpMessageConverters;
5862
import org.springframework.http.converter.HttpMessageConverters.ClientBuilder;
5963
import org.springframework.http.converter.HttpMessageConverters.ServerBuilder;
64+
import org.springframework.http.converter.HttpMessageNotReadableException;
65+
import org.springframework.http.converter.HttpMessageNotWritableException;
6066
import org.springframework.http.converter.StringHttpMessageConverter;
6167
import org.springframework.http.converter.json.GsonHttpMessageConverter;
6268
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
@@ -103,6 +109,14 @@ void jacksonDefaultConverter() {
103109
});
104110
}
105111

112+
@Test
113+
void jacksonServerCustomizer() {
114+
this.contextRunner.withUserConfiguration(CustomJsonConverterConfig.class).run((context) -> {
115+
assertConverterIsNotRegistered(context, JacksonJsonHttpMessageConverter.class);
116+
assertConverterIsRegistered(context, CustomConverter.class);
117+
});
118+
}
119+
106120
@Test
107121
void jacksonConverterWithBuilder() {
108122
this.contextRunner.withUserConfiguration(JacksonJsonMapperBuilderConfig.class).run((context) -> {
@@ -455,16 +469,16 @@ private void assertBeanExists(AssertableApplicationContext context, Class<?> typ
455469

456470
private HttpMessageConverters getClientConverters(ApplicationContext context) {
457471
ClientBuilder clientBuilder = HttpMessageConverters.forClient().registerDefaults();
458-
context.getBeansOfType(ClientHttpMessageConvertersCustomizer.class)
459-
.values()
472+
context.getBeanProvider(ClientHttpMessageConvertersCustomizer.class)
473+
.orderedStream()
460474
.forEach((customizer) -> customizer.customize(clientBuilder));
461475
return clientBuilder.build();
462476
}
463477

464478
private HttpMessageConverters getServerConverters(ApplicationContext context) {
465479
ServerBuilder serverBuilder = HttpMessageConverters.forServer().registerDefaults();
466-
context.getBeansOfType(ServerHttpMessageConvertersCustomizer.class)
467-
.values()
480+
context.getBeanProvider(ServerHttpMessageConvertersCustomizer.class)
481+
.orderedStream()
468482
.forEach((customizer) -> customizer.customize(serverBuilder));
469483
return serverBuilder.build();
470484
}
@@ -503,6 +517,26 @@ JsonMapper jsonMapper() {
503517

504518
}
505519

520+
@Configuration(proxyBeanMethods = false)
521+
static class CustomJsonConverterConfig {
522+
523+
@Bean
524+
JsonMapper jsonMapper() {
525+
return new JsonMapper();
526+
}
527+
528+
@Bean
529+
ServerHttpMessageConvertersCustomizer jsonServerCustomizer() {
530+
return (configurer) -> configurer.withJsonConverter(new CustomConverter(MediaType.APPLICATION_JSON));
531+
}
532+
533+
@Bean
534+
ClientHttpMessageConvertersCustomizer jsonClientCustomizer() {
535+
return (configurer) -> configurer.withJsonConverter(new CustomConverter(MediaType.APPLICATION_JSON));
536+
}
537+
538+
}
539+
506540
@Configuration(proxyBeanMethods = false)
507541
static class JacksonJsonMapperBuilderConfig {
508542

@@ -640,4 +674,30 @@ TypeConstrainedJacksonJsonHttpMessageConverter typeConstrainedConverter() {
640674

641675
}
642676

677+
@SuppressWarnings("NullAway")
678+
static class CustomConverter extends AbstractHttpMessageConverter<Object> {
679+
680+
CustomConverter(MediaType supportedMediaType) {
681+
super(supportedMediaType);
682+
}
683+
684+
@Override
685+
protected boolean supports(Class<?> clazz) {
686+
return true;
687+
}
688+
689+
@Override
690+
protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage)
691+
throws IOException, HttpMessageNotReadableException {
692+
return null;
693+
}
694+
695+
@Override
696+
protected void writeInternal(Object o, HttpOutputMessage outputMessage)
697+
throws IOException, HttpMessageNotWritableException {
698+
699+
}
700+
701+
}
702+
643703
}

0 commit comments

Comments
 (0)