diff --git a/framework-docs/src/main/java/org/springframework/docs/web/webmvc/mvcconfig/mvcconfigmessageconverters/WebConfiguration.java b/framework-docs/src/main/java/org/springframework/docs/web/webmvc/mvcconfig/mvcconfigmessageconverters/WebConfiguration.java index 19f4b52feba..cfdba475159 100644 --- a/framework-docs/src/main/java/org/springframework/docs/web/webmvc/mvcconfig/mvcconfigmessageconverters/WebConfiguration.java +++ b/framework-docs/src/main/java/org/springframework/docs/web/webmvc/mvcconfig/mvcconfigmessageconverters/WebConfiguration.java @@ -34,7 +34,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; public class WebConfiguration implements WebMvcConfigurer { @Override - public void configureMessageConverters(HttpMessageConverters.Builder builder) { + public void configureMessageConverters(HttpMessageConverters.ServerBuilder builder) { JsonMapper jsonMapper = JsonMapper.builder() .findAndAddModules() .enable(SerializationFeature.INDENT_OUTPUT) diff --git a/framework-docs/src/main/kotlin/org/springframework/docs/web/webmvc/mvcconfig/mvcconfigmessageconverters/WebConfiguration.kt b/framework-docs/src/main/kotlin/org/springframework/docs/web/webmvc/mvcconfig/mvcconfigmessageconverters/WebConfiguration.kt index d090603dea6..476f717b462 100644 --- a/framework-docs/src/main/kotlin/org/springframework/docs/web/webmvc/mvcconfig/mvcconfigmessageconverters/WebConfiguration.kt +++ b/framework-docs/src/main/kotlin/org/springframework/docs/web/webmvc/mvcconfig/mvcconfigmessageconverters/WebConfiguration.kt @@ -16,7 +16,7 @@ import java.text.SimpleDateFormat @Configuration class WebConfiguration : WebMvcConfigurer { - override fun configureMessageConverters(builder: HttpMessageConverters.Builder) { + override fun configureMessageConverters(builder: HttpMessageConverters.ServerBuilder) { val jsonMapper = JsonMapper.builder() .findAndAddModules() .enable(SerializationFeature.INDENT_OUTPUT) diff --git a/spring-web/src/main/java/org/springframework/http/converter/DefaultHttpMessageConverters.java b/spring-web/src/main/java/org/springframework/http/converter/DefaultHttpMessageConverters.java index 38a55fbb270..bedffb57fc1 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/DefaultHttpMessageConverters.java +++ b/spring-web/src/main/java/org/springframework/http/converter/DefaultHttpMessageConverters.java @@ -19,6 +19,7 @@ package org.springframework.http.converter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.function.Consumer; @@ -55,113 +56,19 @@ import org.springframework.util.ClassUtils; @SuppressWarnings("removal") class DefaultHttpMessageConverters implements HttpMessageConverters { - private final List> clientMessageConverters; + private final List> messageConverters; - private final List> serverMessageConverters; - - DefaultHttpMessageConverters(List> clientMessageConverters, List> serverMessageConverters) { - this.clientMessageConverters = clientMessageConverters; - this.serverMessageConverters = serverMessageConverters; - } - - @Override - public Iterable> forClient() { - return this.clientMessageConverters; + DefaultHttpMessageConverters(List> messageConverters) { + this.messageConverters = messageConverters; } @Override - public Iterable> forServer() { - return this.serverMessageConverters; + public Iterator> iterator() { + return this.messageConverters.iterator(); } - static class DefaultBuilder implements HttpMessageConverters.Builder { - - private final DefaultMessageConverterConfigurer commonMessageConverters; - - private final DefaultClientMessageConverterConfigurer clientMessageConverterConfigurer; - - private final DefaultServerMessageConverterConfigurer serverMessageConverterConfigurer; - - - DefaultBuilder(boolean registerDefaults) { - this(registerDefaults, DefaultHttpMessageConverters.class.getClassLoader()); - } - - DefaultBuilder(boolean registerDefaults, ClassLoader classLoader) { - this.commonMessageConverters = new DefaultMessageConverterConfigurer(); - this.clientMessageConverterConfigurer = new DefaultClientMessageConverterConfigurer(this.commonMessageConverters); - this.serverMessageConverterConfigurer = new DefaultServerMessageConverterConfigurer(this.commonMessageConverters); - if (registerDefaults) { - this.commonMessageConverters.registerDefaults(); - this.clientMessageConverterConfigurer.registerDefaults(); - this.serverMessageConverterConfigurer.registerDefaults(); - } - } - - @Override - public Builder configureClient(Consumer consumer) { - consumer.accept(this.clientMessageConverterConfigurer); - return this; - } - - @Override - public Builder configureServer(Consumer consumer) { - consumer.accept(this.serverMessageConverterConfigurer); - return this; - } - @Override - public Builder stringMessageConverter(HttpMessageConverter stringMessageConverter) { - this.commonMessageConverters.setStringMessageConverter(stringMessageConverter); - return this; - } - - @Override - public DefaultBuilder jsonMessageConverter(HttpMessageConverter jsonMessageConverter) { - this.commonMessageConverters.setJsonMessageConverter(jsonMessageConverter); - return this; - } - - @Override - public DefaultBuilder xmlMessageConverter(HttpMessageConverter xmlMessageConverter) { - this.commonMessageConverters.setXmlMessageConverter(xmlMessageConverter); - return this; - } - - @Override - public DefaultBuilder smileMessageConverter(HttpMessageConverter smileMessageConverter) { - this.commonMessageConverters.setSmileMessageConverter(smileMessageConverter); - return this; - } - - @Override - public Builder cborMessageConverter(HttpMessageConverter cborMessageConverter) { - this.commonMessageConverters.setCborMessageConverter(cborMessageConverter); - return this; - } - - @Override - public Builder yamlMessageConverter(HttpMessageConverter yamlMessageConverter) { - this.commonMessageConverters.setYamlMessageConverter(yamlMessageConverter); - return this; - } - - @Override - public DefaultBuilder additionalMessageConverter(HttpMessageConverter customConverter) { - Assert.notNull(customConverter, "'customConverter' must not be null"); - this.commonMessageConverters.customMessageConverters.add(customConverter); - return this; - } - - @Override - public DefaultHttpMessageConverters build() { - return new DefaultHttpMessageConverters(this.clientMessageConverterConfigurer.getMessageConverters(), - this.serverMessageConverterConfigurer.getMessageConverters()); - } - - } - - static class DefaultMessageConverterConfigurer { + abstract static class DefaultBuilder { private static final boolean isJacksonPresent; @@ -197,35 +104,37 @@ class DefaultHttpMessageConverters implements HttpMessageConverters { private static final boolean isRomePresent; + boolean registerDefaults; - private final @Nullable DefaultMessageConverterConfigurer inheritedMessageConverters; - - private @Nullable ByteArrayHttpMessageConverter byteArrayMessageConverter; + @Nullable ByteArrayHttpMessageConverter byteArrayMessageConverter; - private @Nullable HttpMessageConverter stringMessageConverter; + @Nullable HttpMessageConverter stringMessageConverter; List> resourceMessageConverters = Collections.emptyList(); - private @Nullable HttpMessageConverter jsonMessageConverter; + @Nullable Consumer> configurer; - private @Nullable HttpMessageConverter xmlMessageConverter; + @Nullable HttpMessageConverter jsonMessageConverter; - private @Nullable HttpMessageConverter smileMessageConverter; + @Nullable HttpMessageConverter xmlMessageConverter; - private @Nullable HttpMessageConverter cborMessageConverter; + @Nullable HttpMessageConverter smileMessageConverter; - private @Nullable HttpMessageConverter yamlMessageConverter; + @Nullable HttpMessageConverter cborMessageConverter; - private @Nullable HttpMessageConverter protobufMessageConverter; + @Nullable HttpMessageConverter yamlMessageConverter; - private @Nullable HttpMessageConverter atomMessageConverter; + @Nullable HttpMessageConverter protobufMessageConverter; - private @Nullable HttpMessageConverter rssMessageConverter; + @Nullable HttpMessageConverter atomMessageConverter; + + @Nullable HttpMessageConverter rssMessageConverter; + + final List> customMessageConverters = new ArrayList<>(); - private final List> customMessageConverters = new ArrayList<>(); static { - ClassLoader classLoader = DefaultClientMessageConverterConfigurer.class.getClassLoader(); + ClassLoader classLoader = DefaultBuilder.class.getClassLoader(); isJacksonPresent = ClassUtils.isPresent("tools.jackson.databind.ObjectMapper", classLoader); isJackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader); @@ -246,13 +155,6 @@ class DefaultHttpMessageConverters implements HttpMessageConverters { isRomePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader); } - DefaultMessageConverterConfigurer() { - this(null); - } - - DefaultMessageConverterConfigurer(@Nullable DefaultMessageConverterConfigurer inheritedMessageConverters) { - this.inheritedMessageConverters = inheritedMessageConverters; - } void setStringMessageConverter(HttpMessageConverter stringMessageConverter) { Assert.isTrue(stringMessageConverter.getSupportedMediaTypes().contains(MediaType.TEXT_PLAIN), @@ -290,22 +192,23 @@ class DefaultHttpMessageConverters implements HttpMessageConverters { this.yamlMessageConverter = yamlMessageConverter; } + void addCustomMessageConverter(HttpMessageConverter customConverter) { + Assert.notNull(customConverter, "'customConverter' must not be null"); + this.customMessageConverters.add(customConverter); + } + + void addMessageConverterConfigurer(Consumer> configurer) { + this.configurer = (this.configurer != null) ? configurer.andThen(this.configurer) : configurer; + } + List> getBaseConverters() { List> converters = new ArrayList<>(); if (this.byteArrayMessageConverter != null) { converters.add(this.byteArrayMessageConverter); } - else if (this.inheritedMessageConverters != null && - this.inheritedMessageConverters.byteArrayMessageConverter != null) { - converters.add(this.inheritedMessageConverters.byteArrayMessageConverter); - } if (this.stringMessageConverter != null) { converters.add(this.stringMessageConverter); } - else if (this.inheritedMessageConverters != null && - this.inheritedMessageConverters.stringMessageConverter != null) { - converters.add(this.inheritedMessageConverters.stringMessageConverter); - } return converters; } @@ -314,305 +217,277 @@ class DefaultHttpMessageConverters implements HttpMessageConverters { if (this.jsonMessageConverter != null) { converters.add(this.jsonMessageConverter); } - else if (this.inheritedMessageConverters != null && - this.inheritedMessageConverters.jsonMessageConverter != null) { - converters.add(this.inheritedMessageConverters.jsonMessageConverter); - } if (this.smileMessageConverter != null) { converters.add(this.smileMessageConverter); } - else if (this.inheritedMessageConverters != null && - this.inheritedMessageConverters.smileMessageConverter != null) { - converters.add(this.inheritedMessageConverters.smileMessageConverter); - } if (this.cborMessageConverter!= null) { converters.add(this.cborMessageConverter); } - else if (this.inheritedMessageConverters != null && - this.inheritedMessageConverters.cborMessageConverter != null) { - converters.add(this.inheritedMessageConverters.cborMessageConverter); - } if (this.yamlMessageConverter!= null) { converters.add(this.yamlMessageConverter); } - else if (this.inheritedMessageConverters != null && - this.inheritedMessageConverters.yamlMessageConverter != null) { - converters.add(this.inheritedMessageConverters.yamlMessageConverter); - } if (this.xmlMessageConverter!= null) { converters.add(this.xmlMessageConverter); } - else if (this.inheritedMessageConverters != null && - this.inheritedMessageConverters.xmlMessageConverter != null) { - converters.add(this.inheritedMessageConverters.xmlMessageConverter); - } if (this.protobufMessageConverter != null) { converters.add(this.protobufMessageConverter); } - else if (this.inheritedMessageConverters != null && - this.inheritedMessageConverters.protobufMessageConverter != null) { - converters.add(this.inheritedMessageConverters.protobufMessageConverter); - } if (this.atomMessageConverter != null) { converters.add(this.atomMessageConverter); } - else if (this.inheritedMessageConverters != null && - this.inheritedMessageConverters.atomMessageConverter != null) { - converters.add(this.inheritedMessageConverters.atomMessageConverter); - } if (this.rssMessageConverter != null) { converters.add(this.rssMessageConverter); } - else if (this.inheritedMessageConverters != null && - this.inheritedMessageConverters.rssMessageConverter != null) { - converters.add(this.inheritedMessageConverters.rssMessageConverter); - } return converters; } List> getCustomConverters() { - List> result = new ArrayList<>(this.customMessageConverters); - if (this.inheritedMessageConverters != null) { - result.addAll(this.inheritedMessageConverters.customMessageConverters); - } - return result; + return this.customMessageConverters; } - void registerDefaults() { + void detectMessageConverters() { this.byteArrayMessageConverter = new ByteArrayHttpMessageConverter(); this.stringMessageConverter = new StringHttpMessageConverter(); - if (isJacksonPresent) { - this.jsonMessageConverter = new JacksonJsonHttpMessageConverter(); - } - else if (isJackson2Present) { - this.jsonMessageConverter = new MappingJackson2HttpMessageConverter(); - } - else if (isGsonPresent) { - this.jsonMessageConverter = new GsonHttpMessageConverter(); - } - else if (isJsonbPresent) { - this.jsonMessageConverter = new JsonbHttpMessageConverter(); - } - else if (isKotlinSerializationJsonPresent) { - this.jsonMessageConverter = new KotlinSerializationJsonHttpMessageConverter(); - } - - if (isJacksonXmlPresent) { - this.xmlMessageConverter = new JacksonXmlHttpMessageConverter(); - } - else if (isJackson2XmlPresent) { - this.xmlMessageConverter = new MappingJackson2XmlHttpMessageConverter(); - } - else if (isJaxb2Present) { - this.xmlMessageConverter = new Jaxb2RootElementHttpMessageConverter(); - } - - if (isJacksonSmilePresent) { - this.smileMessageConverter = new JacksonSmileHttpMessageConverter(); - } - else if (isJackson2SmilePresent) { - this.smileMessageConverter = new MappingJackson2SmileHttpMessageConverter(); - } - if (isJacksonCborPresent) { - this.cborMessageConverter = new JacksonCborHttpMessageConverter(); - } - else if (isJackson2CborPresent) { - this.cborMessageConverter = new MappingJackson2CborHttpMessageConverter(); - } - else if (isKotlinSerializationCborPresent) { - this.cborMessageConverter = new KotlinSerializationCborHttpMessageConverter(); - } - - if (isJacksonYamlPresent) { - this.yamlMessageConverter = new JacksonYamlHttpMessageConverter(); - } - else if (isJackson2YamlPresent) { - this.yamlMessageConverter = new MappingJackson2YamlHttpMessageConverter(); - } - - if (isKotlinSerializationProtobufPresent) { - this.protobufMessageConverter = new KotlinSerializationProtobufHttpMessageConverter(); + if (this.jsonMessageConverter == null) { + if (isJacksonPresent) { + this.jsonMessageConverter = new JacksonJsonHttpMessageConverter(); + } + else if (isJackson2Present) { + this.jsonMessageConverter = new MappingJackson2HttpMessageConverter(); + } + else if (isGsonPresent) { + this.jsonMessageConverter = new GsonHttpMessageConverter(); + } + else if (isJsonbPresent) { + this.jsonMessageConverter = new JsonbHttpMessageConverter(); + } + else if (isKotlinSerializationJsonPresent) { + this.jsonMessageConverter = new KotlinSerializationJsonHttpMessageConverter(); + } + } + + if (this.xmlMessageConverter == null) { + if (isJacksonXmlPresent) { + this.xmlMessageConverter = new JacksonXmlHttpMessageConverter(); + } + else if (isJackson2XmlPresent) { + this.xmlMessageConverter = new MappingJackson2XmlHttpMessageConverter(); + } + else if (isJaxb2Present) { + this.xmlMessageConverter = new Jaxb2RootElementHttpMessageConverter(); + } + } + + if (this.smileMessageConverter == null) { + if (isJacksonSmilePresent) { + this.smileMessageConverter = new JacksonSmileHttpMessageConverter(); + } + else if (isJackson2SmilePresent) { + this.smileMessageConverter = new MappingJackson2SmileHttpMessageConverter(); + } + } + + if (this.cborMessageConverter == null) { + if (isJacksonCborPresent) { + this.cborMessageConverter = new JacksonCborHttpMessageConverter(); + } + else if (isJackson2CborPresent) { + this.cborMessageConverter = new MappingJackson2CborHttpMessageConverter(); + } + else if (isKotlinSerializationCborPresent) { + this.cborMessageConverter = new KotlinSerializationCborHttpMessageConverter(); + } + } + + if (this.yamlMessageConverter == null) { + if (isJacksonYamlPresent) { + this.yamlMessageConverter = new JacksonYamlHttpMessageConverter(); + } + else if (isJackson2YamlPresent) { + this.yamlMessageConverter = new MappingJackson2YamlHttpMessageConverter(); + } + } + + if (this.protobufMessageConverter == null) { + if (isKotlinSerializationProtobufPresent) { + this.protobufMessageConverter = new KotlinSerializationProtobufHttpMessageConverter(); + } } if (isRomePresent) { - this.atomMessageConverter = new AtomFeedHttpMessageConverter(); - this.rssMessageConverter = new RssChannelHttpMessageConverter(); + if (this.atomMessageConverter == null) { + this.atomMessageConverter = new AtomFeedHttpMessageConverter(); + } + if (this.rssMessageConverter == null) { + this.rssMessageConverter = new RssChannelHttpMessageConverter(); + } } } } - static class DefaultClientMessageConverterConfigurer extends DefaultMessageConverterConfigurer implements ClientMessageConverterConfigurer { - - private @Nullable Consumer> configurer; - - private final DefaultMessageConverterConfigurer clientMessageConverters; - - - public DefaultClientMessageConverterConfigurer(DefaultMessageConverterConfigurer parentMessageConverters) { - this.clientMessageConverters = new DefaultMessageConverterConfigurer(parentMessageConverters); - } + static class DefaultClientBuilder extends DefaultBuilder implements ClientBuilder { @Override - public ClientMessageConverterConfigurer stringMessageConverter(HttpMessageConverter stringMessageConverter) { - this.clientMessageConverters.setStringMessageConverter(stringMessageConverter); + public DefaultClientBuilder registerDefaults() { + this.registerDefaults = true; return this; } @Override - public ClientMessageConverterConfigurer jsonMessageConverter(HttpMessageConverter jsonMessageConverter) { - this.clientMessageConverters.setJsonMessageConverter(jsonMessageConverter); + public ClientBuilder stringMessageConverter(HttpMessageConverter stringMessageConverter) { + setStringMessageConverter(stringMessageConverter); return this; } @Override - public ClientMessageConverterConfigurer xmlMessageConverter(HttpMessageConverter xmlMessageConverter) { - this.clientMessageConverters.setXmlMessageConverter(xmlMessageConverter); + public ClientBuilder jsonMessageConverter(HttpMessageConverter jsonMessageConverter) { + setJsonMessageConverter(jsonMessageConverter); return this; } @Override - public ClientMessageConverterConfigurer smileMessageConverter(HttpMessageConverter smileMessageConverter) { - this.clientMessageConverters.setSmileMessageConverter(smileMessageConverter); + public ClientBuilder xmlMessageConverter(HttpMessageConverter xmlMessageConverter) { + setXmlMessageConverter(xmlMessageConverter); return this; } @Override - public ClientMessageConverterConfigurer cborMessageConverter(HttpMessageConverter cborMessageConverter) { - this.clientMessageConverters.setCborMessageConverter(cborMessageConverter); + public ClientBuilder smileMessageConverter(HttpMessageConverter smileMessageConverter) { + setSmileMessageConverter(smileMessageConverter); return this; } @Override - public ClientMessageConverterConfigurer yamlMessageConverter(HttpMessageConverter yamlMessageConverter) { - this.clientMessageConverters.setYamlMessageConverter(yamlMessageConverter); + public ClientBuilder cborMessageConverter(HttpMessageConverter cborMessageConverter) { + setCborMessageConverter(cborMessageConverter); return this; } @Override - public ClientMessageConverterConfigurer additionalMessageConverter(HttpMessageConverter customConverter) { - Assert.notNull(customConverter, "'customConverter' must not be null"); - this.clientMessageConverters.customMessageConverters.add(customConverter); + public ClientBuilder yamlMessageConverter(HttpMessageConverter yamlMessageConverter) { + setYamlMessageConverter(yamlMessageConverter); return this; } @Override - public ClientMessageConverterConfigurer configureClientMessageConverters(Consumer> configurer) { - this.configurer = (this.configurer != null) ? configurer.andThen(this.configurer) : configurer; + public ClientBuilder customMessageConverter(HttpMessageConverter customConverter) { + addCustomMessageConverter(customConverter); return this; } @Override - void registerDefaults() { - this.resourceMessageConverters = Collections.singletonList(new ResourceHttpMessageConverter(false)); + public ClientBuilder configureMessageConverters(Consumer> configurer) { + addMessageConverterConfigurer(configurer); + return this; } - List> getMessageConverters() { + @Override + public HttpMessageConverters build() { + if (this.registerDefaults) { + this.resourceMessageConverters = Collections.singletonList(new ResourceHttpMessageConverter(false)); + detectMessageConverters(); + } List> allConverters = new ArrayList<>(); List> partConverters = new ArrayList<>(); + partConverters.addAll(this.getCustomConverters()); + partConverters.addAll(this.getCoreConverters()); - partConverters.addAll(this.clientMessageConverters.getCustomConverters()); - partConverters.addAll(this.clientMessageConverters.getCoreConverters()); - - allConverters.addAll(this.clientMessageConverters.getCustomConverters()); - allConverters.addAll(this.clientMessageConverters.getBaseConverters()); + allConverters.addAll(this.getCustomConverters()); + allConverters.addAll(this.getBaseConverters()); allConverters.addAll(this.resourceMessageConverters); if (!partConverters.isEmpty()) { allConverters.add(new AllEncompassingFormHttpMessageConverter(partConverters)); } - allConverters.addAll(this.clientMessageConverters.getCoreConverters()); - + allConverters.addAll(this.getCoreConverters()); if (this.configurer != null) { allConverters.forEach(this.configurer); } - return allConverters; + return new DefaultHttpMessageConverters(allConverters); } } - static class DefaultServerMessageConverterConfigurer extends DefaultMessageConverterConfigurer implements ServerMessageConverterConfigurer { - - private @Nullable Consumer> configurer; - - private final DefaultMessageConverterConfigurer serverMessageConverters; + static class DefaultServerBuilder extends DefaultBuilder implements ServerBuilder { - DefaultServerMessageConverterConfigurer(DefaultMessageConverterConfigurer commonMessageConverters) { - this.serverMessageConverters = new DefaultMessageConverterConfigurer(commonMessageConverters); - } - @Override - public ServerMessageConverterConfigurer stringMessageConverter(HttpMessageConverter stringMessageConverter) { - this.serverMessageConverters.setStringMessageConverter(stringMessageConverter); + public ServerBuilder registerDefaults() { + this.registerDefaults = true; return this; } @Override - public ServerMessageConverterConfigurer jsonMessageConverter(HttpMessageConverter jsonMessageConverter) { - this.serverMessageConverters.setJsonMessageConverter(jsonMessageConverter); + public ServerBuilder stringMessageConverter(HttpMessageConverter stringMessageConverter) { + setStringMessageConverter(stringMessageConverter); return this; } @Override - public ServerMessageConverterConfigurer xmlMessageConverter(HttpMessageConverter xmlMessageConverter) { - this.serverMessageConverters.setXmlMessageConverter(xmlMessageConverter); + public ServerBuilder jsonMessageConverter(HttpMessageConverter jsonMessageConverter) { + setJsonMessageConverter(jsonMessageConverter); return this; } @Override - public ServerMessageConverterConfigurer smileMessageConverter(HttpMessageConverter smileMessageConverter) { - this.serverMessageConverters.setSmileMessageConverter(smileMessageConverter); + public ServerBuilder xmlMessageConverter(HttpMessageConverter xmlMessageConverter) { + setXmlMessageConverter(xmlMessageConverter); return this; } @Override - public ServerMessageConverterConfigurer cborMessageConverter(HttpMessageConverter cborMessageConverter) { - this.serverMessageConverters.setCborMessageConverter(cborMessageConverter); + public ServerBuilder smileMessageConverter(HttpMessageConverter smileMessageConverter) { + setSmileMessageConverter(smileMessageConverter); return this; } @Override - public ServerMessageConverterConfigurer yamlMessageConverter(HttpMessageConverter yamlMessageConverter) { - this.serverMessageConverters.setYamlMessageConverter(yamlMessageConverter); + public ServerBuilder cborMessageConverter(HttpMessageConverter cborMessageConverter) { + setCborMessageConverter(cborMessageConverter); return this; } @Override - public ServerMessageConverterConfigurer additionalMessageConverter(HttpMessageConverter customConverter) { - Assert.notNull(customConverter, "'customConverter' must not be null"); - this.serverMessageConverters.customMessageConverters.add(customConverter); + public ServerBuilder yamlMessageConverter(HttpMessageConverter yamlMessageConverter) { + setYamlMessageConverter(yamlMessageConverter); return this; } @Override - public ServerMessageConverterConfigurer configureServerMessageConverters(Consumer> configurer) { - this.configurer = (this.configurer != null) ? configurer.andThen(this.configurer) : configurer; + public ServerBuilder customMessageConverter(HttpMessageConverter customConverter) { + addCustomMessageConverter(customConverter); return this; } @Override - void registerDefaults() { - this.resourceMessageConverters = Arrays.asList(new ResourceHttpMessageConverter(), new ResourceRegionHttpMessageConverter()); + public ServerBuilder configureMessageConverters(Consumer> configurer) { + addMessageConverterConfigurer(configurer); + return this; } - List> getMessageConverters() { + @Override + public HttpMessageConverters build() { + if (this.registerDefaults) { + this.resourceMessageConverters = Arrays.asList(new ResourceHttpMessageConverter(), new ResourceRegionHttpMessageConverter()); + detectMessageConverters(); + } List> allConverters = new ArrayList<>(); List> partConverters = new ArrayList<>(); - partConverters.addAll(this.serverMessageConverters.getCustomConverters()); - partConverters.addAll(this.serverMessageConverters.getCoreConverters()); + partConverters.addAll(this.getCustomConverters()); + partConverters.addAll(this.getCoreConverters()); - allConverters.addAll(this.serverMessageConverters.getCustomConverters()); - allConverters.addAll(this.serverMessageConverters.getBaseConverters()); + allConverters.addAll(this.getCustomConverters()); + allConverters.addAll(this.getBaseConverters()); allConverters.addAll(this.resourceMessageConverters); if (!partConverters.isEmpty()) { allConverters.add(new AllEncompassingFormHttpMessageConverter(partConverters)); } - allConverters.addAll(this.serverMessageConverters.getCoreConverters()); + allConverters.addAll(this.getCoreConverters()); if (this.configurer != null) { allConverters.forEach(this.configurer); } - return allConverters; + return new DefaultHttpMessageConverters(allConverters); } } diff --git a/spring-web/src/main/java/org/springframework/http/converter/HttpMessageConverters.java b/spring-web/src/main/java/org/springframework/http/converter/HttpMessageConverters.java index 9ff9f07274c..863d6addd6f 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/HttpMessageConverters.java +++ b/spring-web/src/main/java/org/springframework/http/converter/HttpMessageConverters.java @@ -20,79 +20,69 @@ import java.util.function.Consumer; /** * Utility for building and configuring an immutable collection of {@link HttpMessageConverter} - * instances for client and server usage. You can {@link #create() create} - * a new empty instance or ask to {@link #withDefaults() register default converters}, - * if available in your classpath. - * - *

This class offers a flexible arrangement for {@link HttpMessageConverters.Builder configuring message converters shared between} - * client and server, or {@link HttpMessageConverters.Builder#configureClient(Consumer) configuring client-specific} - * and {@link HttpMessageConverters.Builder#configureServer(Consumer) server-specific} converters. - * - *

The following HTTP message converters will be detected and registered if available, in order. - * For {@link #forClient() client side converters}: - *

    - *
  1. All custom message converters configured with the builder - *
  2. {@link ByteArrayHttpMessageConverter} - *
  3. {@link StringHttpMessageConverter} with the {@link java.nio.charset.StandardCharsets#ISO_8859_1} charset - *
  4. {@link ResourceHttpMessageConverter}, with resource streaming support disabled - *
  5. a Multipart converter, using all detected and custom converters for part conversion - *
  6. A JSON converter - *
  7. A Smile converter - *
  8. A CBOR converter - *
  9. A YAML converter - *
  10. An XML converter - *
  11. An ProtoBuf converter - *
  12. ATOM and RSS converters - *
- * - * For {@link #forClient() client side converters}: - *
    - *
  1. All custom message converters configured with the builder - *
  2. {@link ByteArrayHttpMessageConverter} - *
  3. {@link StringHttpMessageConverter} with the {@link java.nio.charset.StandardCharsets#ISO_8859_1} charset - *
  4. {@link ResourceHttpMessageConverter} - *
  5. {@link ResourceRegionHttpMessageConverter} - *
  6. A JSON converter - *
  7. A Smile converter - *
  8. A CBOR converter - *
  9. A YAML converter - *
  10. An XML converter - *
  11. An ProtoBuf converter - *
  12. ATOM and RSS converters - *
  13. a Multipart converter, using all detected and custom converters for part conversion - *
+ * instances for {@link #forClient() client} or {@link #forServer() server} usage. You can + * ask to {@link Builder#registerDefaults() register default converters with classpath detection}, + * add custom converters and post-process configured converters. * * @author Brian Clozel * @since 7.0 */ -public interface HttpMessageConverters { +public interface HttpMessageConverters extends Iterable> { - /** - * Return the list of configured message converters, tailored for HTTP client usage. - */ - Iterable> forClient(); - - /** - * Return the list of configured message converters, tailored for HTTP server usage. - */ - Iterable> forServer(); /** - * Create a builder instance, without any message converter pre-configured. + * Create a builder instance, tailored for HTTP client usage. + *

The following HTTP message converters can be detected and registered if available, in order: + *

    + *
  1. All custom message converters configured with the builder + *
  2. {@link ByteArrayHttpMessageConverter} + *
  3. {@link StringHttpMessageConverter} with the {@link java.nio.charset.StandardCharsets#ISO_8859_1} charset + *
  4. {@link ResourceHttpMessageConverter}, with resource streaming support disabled + *
  5. a Multipart converter, using all detected and custom converters for part conversion + *
  6. A JSON converter + *
  7. A Smile converter + *
  8. A CBOR converter + *
  9. A YAML converter + *
  10. An XML converter + *
  11. A ProtoBuf converter + *
  12. ATOM and RSS converters + *
*/ - static Builder create() { - return new DefaultHttpMessageConverters.DefaultBuilder(false); + static ClientBuilder forClient() { + return new DefaultHttpMessageConverters.DefaultClientBuilder(); } /** - * Create a builder instance with default message converters pre-configured. + * Create a builder instance, tailored for HTTP server usage. + *

The following HTTP message converters can be detected and registered if available, in order: + *

    + *
  1. All custom message converters configured with the builder + *
  2. {@link ByteArrayHttpMessageConverter} + *
  3. {@link StringHttpMessageConverter} with the {@link java.nio.charset.StandardCharsets#ISO_8859_1} charset + *
  4. {@link ResourceHttpMessageConverter} + *
  5. {@link ResourceRegionHttpMessageConverter} + *
  6. A JSON converter + *
  7. A Smile converter + *
  8. A CBOR converter + *
  9. A YAML converter + *
  10. An XML converter + *
  11. A ProtoBuf converter + *
  12. ATOM and RSS converters + *
  13. a Multipart converter, using all detected and custom converters for part conversion + *
*/ - static Builder withDefaults() { - return new DefaultHttpMessageConverters.DefaultBuilder(true); + static ServerBuilder forServer() { + return new DefaultHttpMessageConverters.DefaultServerBuilder(); } + interface Builder> { - interface MessageConverterConfigurer> { + /** + * Register default converters using classpath detection. + * Manual registrations like {@link #jsonMessageConverter(HttpMessageConverter)} will + * override auto-detected ones. + */ + T registerDefaults(); /** * Override the default String {@code HttpMessageConverter} @@ -146,27 +136,13 @@ public interface HttpMessageConverters { * Add a custom {@code HttpMessageConverter} to the list of converters. * @param customConverter the converter instance to add */ - T additionalMessageConverter(HttpMessageConverter customConverter); - - } - - /** - * Builder for an {@link HttpMessageConverters}. - * This builder manages the configuration of common and client/server-specific message converters. - */ - interface Builder extends MessageConverterConfigurer { - - /** - * Configure client-specific message converters. - * If no opinion is provided here, message converters defined in this builder will be used. - */ - Builder configureClient(Consumer consumer); + T customMessageConverter(HttpMessageConverter customConverter); /** - * Configure server-specific message converters. - * If no opinion is provided here, message converters defined in this builder will be used. + * Add a consumer for configuring the selected message converters. + * @param configurer the configurer to use */ - Builder configureServer(Consumer consumer); + T configureMessageConverters(Consumer> configurer); /** * Build and return the {@link HttpMessageConverters} instance configured by this builder. @@ -174,25 +150,17 @@ public interface HttpMessageConverters { HttpMessageConverters build(); } - interface ClientMessageConverterConfigurer extends MessageConverterConfigurer { - - /** - * Register a consumer to apply to configured converter instances. - * This can be used to configure rather than replace one or more specific converters. - * @param configurer the consumer to apply - */ - ClientMessageConverterConfigurer configureClientMessageConverters(Consumer> configurer); + /** + * Client builder for an {@link HttpMessageConverters} instance. + */ + interface ClientBuilder extends Builder { } - interface ServerMessageConverterConfigurer extends MessageConverterConfigurer { - - /** - * Register a consumer to apply to configured converter instances. - * This can be used to configure rather than replace one or more specific converters. - * @param configurer the consumer to apply - */ - ServerMessageConverterConfigurer configureServerMessageConverters(Consumer> configurer); + /** + * Server builder for an {@link HttpMessageConverters} instance. + */ + interface ServerBuilder extends Builder { } diff --git a/spring-web/src/main/java/org/springframework/http/converter/support/AllEncompassingFormHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/support/AllEncompassingFormHttpMessageConverter.java index 9aac205cd44..24a6ad19044 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/support/AllEncompassingFormHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/support/AllEncompassingFormHttpMessageConverter.java @@ -39,7 +39,7 @@ public class AllEncompassingFormHttpMessageConverter extends FormHttpMessageConv */ @SuppressWarnings("removal") public AllEncompassingFormHttpMessageConverter() { - HttpMessageConverters.withDefaults().build().forClient().forEach(this::addPartConverter); + HttpMessageConverters.forClient().registerDefaults().build().forEach(this::addPartConverter); } /** diff --git a/spring-web/src/main/java/org/springframework/web/client/DefaultRestClientBuilder.java b/spring-web/src/main/java/org/springframework/web/client/DefaultRestClientBuilder.java index 945598fd42d..ae1904b2641 100644 --- a/spring-web/src/main/java/org/springframework/web/client/DefaultRestClientBuilder.java +++ b/spring-web/src/main/java/org/springframework/web/client/DefaultRestClientBuilder.java @@ -361,6 +361,7 @@ final class DefaultRestClientBuilder implements RestClient.Builder { } @Override + @SuppressWarnings("removal") public RestClient.Builder messageConverters(Consumer>> configurer) { configurer.accept(initMessageConverters()); validateConverters(this.messageConverters); @@ -376,6 +377,13 @@ final class DefaultRestClientBuilder implements RestClient.Builder { return this; } + @Override + public RestClient.Builder configureMessageConverters(Consumer configurer) { + HttpMessageConverters.ClientBuilder clientBuilder = HttpMessageConverters.forClient(); + configurer.accept(clientBuilder); + return messageConverters(clientBuilder.build()); + } + @Override public RestClient.Builder observationRegistry(ObservationRegistry observationRegistry) { Assert.notNull(observationRegistry, "observationRegistry must not be null"); @@ -399,7 +407,7 @@ final class DefaultRestClientBuilder implements RestClient.Builder { private List> initMessageConverters() { if (this.messageConverters == null) { this.messageConverters = new ArrayList<>(); - HttpMessageConverters.withDefaults().build().forClient().forEach(this.messageConverters::add); + HttpMessageConverters.forClient().registerDefaults().build().forEach(this.messageConverters::add); } return this.messageConverters; } diff --git a/spring-web/src/main/java/org/springframework/web/client/RestClient.java b/spring-web/src/main/java/org/springframework/web/client/RestClient.java index d9f8b1f96fc..bb7e6180b2a 100644 --- a/spring-web/src/main/java/org/springframework/web/client/RestClient.java +++ b/spring-web/src/main/java/org/springframework/web/client/RestClient.java @@ -47,6 +47,7 @@ import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.observation.ClientRequestObservationConvention; import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.HttpMessageConverters; import org.springframework.lang.CheckReturnValue; import org.springframework.util.MultiValueMap; import org.springframework.web.util.DefaultUriBuilderFactory; @@ -452,7 +453,9 @@ public interface RestClient { * {@link HttpMessageConverter} pre-initialized * @return this builder * @see #messageConverters(Iterable) + * @deprecated since 7.0 in favor of {@link #configureMessageConverters(Consumer)} */ + @Deprecated(since = "7.0", forRemoval = true) Builder messageConverters(Consumer>> configurer); /** @@ -460,10 +463,18 @@ public interface RestClient { * @param messageConverters the list of {@link HttpMessageConverter} to use * @return this builder * @since 6.2 - * @see #messageConverters(Consumer) + * @see #configureMessageConverters(Consumer) */ Builder messageConverters(Iterable> messageConverters); + /** + * Configure the message converters for the {@code RestClient} to use. + * @param configurer the configurer to apply on an empty {@link HttpMessageConverters.ClientBuilder}. + * @return this builder + * @since 7.0 + */ + Builder configureMessageConverters(Consumer configurer); + /** * Configure the {@link io.micrometer.observation.ObservationRegistry} to use * for recording HTTP client observations. diff --git a/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java b/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java index 5861306e2a0..7b8558a51e3 100644 --- a/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java +++ b/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java @@ -124,9 +124,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat * Default {@link HttpMessageConverter HttpMessageConverters} are initialized. */ public RestTemplate() { - HttpMessageConverters.withDefaults().build().forClient().forEach(this.messageConverters::add); - updateErrorHandlerConverters(); - this.uriTemplateHandler = initUriTemplateHandler(); + this(HttpMessageConverters.forClient().registerDefaults().build()); } /** diff --git a/spring-web/src/test/java/org/springframework/http/converter/DefaultHttpMessageConvertersTests.java b/spring-web/src/test/java/org/springframework/http/converter/DefaultHttpMessageConvertersTests.java index 1598ee4bb24..9147b968869 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/DefaultHttpMessageConvertersTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/DefaultHttpMessageConvertersTests.java @@ -17,11 +17,6 @@ package org.springframework.http.converter; import java.io.IOException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; import java.util.stream.Stream; import java.util.stream.StreamSupport; @@ -30,7 +25,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import org.springframework.core.SmartClassLoader; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.converter.cbor.JacksonCborHttpMessageConverter; @@ -60,59 +54,50 @@ class DefaultHttpMessageConvertersTests { static Stream>> emptyMessageConverters() { return Stream.of( - HttpMessageConverters.create().build().forClient(), - HttpMessageConverters.create().build().forServer() + HttpMessageConverters.forClient().build(), + HttpMessageConverters.forServer().build() ); } - @Test - void clientAndServerConvertersAreShared() { - var converters = HttpMessageConverters.withDefaults().build(); - Set> allConverters = new HashSet<>(); - converters.forClient().forEach(allConverters::add); - converters.forServer().forEach(allConverters::add); - assertThat(allConverters).hasSize(15); - } - @Test void failsWhenStringConverterDoesNotSupportMediaType() { assertThatIllegalArgumentException() - .isThrownBy(() -> HttpMessageConverters.create().stringMessageConverter(new CustomHttpMessageConverter()).build()) + .isThrownBy(() -> HttpMessageConverters.forClient().stringMessageConverter(new CustomHttpMessageConverter()).build()) .withMessage("stringMessageConverter should support 'text/plain'"); } @Test void failsWhenJsonConverterDoesNotSupportMediaType() { assertThatIllegalArgumentException() - .isThrownBy(() -> HttpMessageConverters.create().jsonMessageConverter(new CustomHttpMessageConverter()).build()) + .isThrownBy(() -> HttpMessageConverters.forClient().jsonMessageConverter(new CustomHttpMessageConverter()).build()) .withMessage("jsonMessageConverter should support 'application/json'"); } @Test void failsWhenXmlConverterDoesNotSupportMediaType() { assertThatIllegalArgumentException() - .isThrownBy(() -> HttpMessageConverters.create().xmlMessageConverter(new CustomHttpMessageConverter()).build()) + .isThrownBy(() -> HttpMessageConverters.forClient().xmlMessageConverter(new CustomHttpMessageConverter()).build()) .withMessage("xmlMessageConverter should support 'text/xml'"); } @Test void failsWhenSmileConverterDoesNotSupportMediaType() { assertThatIllegalArgumentException() - .isThrownBy(() -> HttpMessageConverters.create().smileMessageConverter(new CustomHttpMessageConverter()).build()) + .isThrownBy(() -> HttpMessageConverters.forClient().smileMessageConverter(new CustomHttpMessageConverter()).build()) .withMessage("smileMessageConverter should support 'application/x-jackson-smile'"); } @Test void failsWhenCborConverterDoesNotSupportMediaType() { assertThatIllegalArgumentException() - .isThrownBy(() -> HttpMessageConverters.create().cborMessageConverter(new CustomHttpMessageConverter()).build()) + .isThrownBy(() -> HttpMessageConverters.forClient().cborMessageConverter(new CustomHttpMessageConverter()).build()) .withMessage("cborMessageConverter should support 'application/cbor'"); } @Test void failsWhenYamlConverterDoesNotSupportMediaType() { assertThatIllegalArgumentException() - .isThrownBy(() -> HttpMessageConverters.create().yamlMessageConverter(new CustomHttpMessageConverter()).build()) + .isThrownBy(() -> HttpMessageConverters.forClient().yamlMessageConverter(new CustomHttpMessageConverter()).build()) .withMessage("yamlMessageConverter should support 'application/yaml'"); } @@ -122,8 +107,8 @@ class DefaultHttpMessageConvertersTests { @Test void defaultConverters() { - var converters = HttpMessageConverters.withDefaults().build(); - assertThat(converters.forClient()).hasExactlyElementsOfTypes(ByteArrayHttpMessageConverter.class, + var converters = HttpMessageConverters.forClient().registerDefaults().build(); + assertThat(converters).hasExactlyElementsOfTypes(ByteArrayHttpMessageConverter.class, StringHttpMessageConverter.class, ResourceHttpMessageConverter.class, AllEncompassingFormHttpMessageConverter.class, JacksonJsonHttpMessageConverter.class, JacksonSmileHttpMessageConverter.class, @@ -134,8 +119,8 @@ class DefaultHttpMessageConvertersTests { @Test void multipartConverterContainsOtherConverters() { - var converters = HttpMessageConverters.withDefaults().build(); - AllEncompassingFormHttpMessageConverter multipartConverter = findMessageConverter(AllEncompassingFormHttpMessageConverter.class, converters.forClient()); + var converters = HttpMessageConverters.forClient().registerDefaults().build(); + var multipartConverter = findMessageConverter(AllEncompassingFormHttpMessageConverter.class, converters); assertThat(multipartConverter.getPartConverters()).hasExactlyElementsOfTypes( ByteArrayHttpMessageConverter.class, StringHttpMessageConverter.class, @@ -148,16 +133,16 @@ class DefaultHttpMessageConvertersTests { @Test void registerCustomMessageConverter() { - var converters = HttpMessageConverters.create() - .additionalMessageConverter(new CustomHttpMessageConverter()).build(); - assertThat(converters.forClient()).hasExactlyElementsOfTypes(CustomHttpMessageConverter.class, AllEncompassingFormHttpMessageConverter.class); + var converters = HttpMessageConverters.forClient() + .customMessageConverter(new CustomHttpMessageConverter()).build(); + assertThat(converters).hasExactlyElementsOfTypes(CustomHttpMessageConverter.class, AllEncompassingFormHttpMessageConverter.class); } @Test void registerCustomMessageConverterAheadOfDefaults() { - var converters = HttpMessageConverters.withDefaults() - .additionalMessageConverter(new CustomHttpMessageConverter()).build(); - assertThat(converters.forClient()).hasExactlyElementsOfTypes( + var converters = HttpMessageConverters.forClient().registerDefaults() + .customMessageConverter(new CustomHttpMessageConverter()).build(); + assertThat(converters).hasExactlyElementsOfTypes( CustomHttpMessageConverter.class, ByteArrayHttpMessageConverter.class, StringHttpMessageConverter.class, ResourceHttpMessageConverter.class, AllEncompassingFormHttpMessageConverter.class, @@ -169,33 +154,31 @@ class DefaultHttpMessageConvertersTests { @Test void registerCustomConverterInMultipartConverter() { - var converters = HttpMessageConverters.withDefaults() - .additionalMessageConverter(new CustomHttpMessageConverter()).build(); - AllEncompassingFormHttpMessageConverter multipartConverter = findMessageConverter(AllEncompassingFormHttpMessageConverter.class, converters.forClient()); + var converters = HttpMessageConverters.forClient().registerDefaults() + .customMessageConverter(new CustomHttpMessageConverter()).build(); + var multipartConverter = findMessageConverter(AllEncompassingFormHttpMessageConverter.class, converters); assertThat(multipartConverter.getPartConverters()).hasAtLeastOneElementOfType(CustomHttpMessageConverter.class); } @Test - void shouldUseServerSpecificConverter() { - JacksonJsonHttpMessageConverter jacksonConverter = new JacksonJsonHttpMessageConverter(); - var converters = HttpMessageConverters.withDefaults() - .configureClient(configurer -> configurer.jsonMessageConverter(jacksonConverter)).build(); + void shouldUseSpecificConverter() { + var jacksonConverter = new JacksonJsonHttpMessageConverter(); + var converters = HttpMessageConverters.forClient().registerDefaults() + .jsonMessageConverter(jacksonConverter).build(); - JacksonJsonHttpMessageConverter customConverter = findMessageConverter(JacksonJsonHttpMessageConverter.class, converters.forClient()); + var customConverter = findMessageConverter(JacksonJsonHttpMessageConverter.class, converters); assertThat(customConverter).isEqualTo(jacksonConverter); } @Test void shouldConfigureConverter() { - CustomHttpMessageConverter customConverter = new CustomHttpMessageConverter(); - HttpMessageConverters.withDefaults() - .additionalMessageConverter(customConverter) - .configureClient(configurer -> { - configurer.configureClientMessageConverters(converter -> { - if (converter instanceof CustomHttpMessageConverter custom) { - custom.processed = true; - } - }); + var customConverter = new CustomHttpMessageConverter(); + HttpMessageConverters.forClient() + .customMessageConverter(customConverter) + .configureMessageConverters(converter -> { + if (converter instanceof CustomHttpMessageConverter custom) { + custom.processed = true; + } }).build(); assertThat(customConverter.processed).isTrue(); @@ -209,8 +192,8 @@ class DefaultHttpMessageConvertersTests { @Test void defaultConverters() { - var converters = HttpMessageConverters.withDefaults().build(); - assertThat(converters.forServer()).hasExactlyElementsOfTypes( + var converters = HttpMessageConverters.forServer().registerDefaults().build(); + assertThat(converters).hasExactlyElementsOfTypes( ByteArrayHttpMessageConverter.class, StringHttpMessageConverter.class, ResourceHttpMessageConverter.class, ResourceRegionHttpMessageConverter.class, AllEncompassingFormHttpMessageConverter.class, @@ -222,8 +205,8 @@ class DefaultHttpMessageConvertersTests { @Test void multipartConverterContainsOtherConverters() { - var converters = HttpMessageConverters.withDefaults().build(); - AllEncompassingFormHttpMessageConverter multipartConverter = findMessageConverter(AllEncompassingFormHttpMessageConverter.class, converters.forServer()); + var converters = HttpMessageConverters.forServer().registerDefaults().build(); + var multipartConverter = findMessageConverter(AllEncompassingFormHttpMessageConverter.class, converters); assertThat(multipartConverter.getPartConverters()).hasExactlyElementsOfTypes( ByteArrayHttpMessageConverter.class, StringHttpMessageConverter.class, @@ -236,16 +219,16 @@ class DefaultHttpMessageConvertersTests { @Test void registerCustomMessageConverter() { - var converters = HttpMessageConverters.create() - .additionalMessageConverter(new CustomHttpMessageConverter()).build(); - assertThat(converters.forServer()).hasExactlyElementsOfTypes(CustomHttpMessageConverter.class, AllEncompassingFormHttpMessageConverter.class); + var converters = HttpMessageConverters.forServer() + .customMessageConverter(new CustomHttpMessageConverter()).build(); + assertThat(converters).hasExactlyElementsOfTypes(CustomHttpMessageConverter.class, AllEncompassingFormHttpMessageConverter.class); } @Test void registerCustomMessageConverterAheadOfDefaults() { - var converters = HttpMessageConverters.withDefaults() - .additionalMessageConverter(new CustomHttpMessageConverter()).build(); - assertThat(converters.forServer()).hasExactlyElementsOfTypes( + var converters = HttpMessageConverters.forServer().registerDefaults() + .customMessageConverter(new CustomHttpMessageConverter()).build(); + assertThat(converters).hasExactlyElementsOfTypes( CustomHttpMessageConverter.class, ByteArrayHttpMessageConverter.class, StringHttpMessageConverter.class, ResourceHttpMessageConverter.class, ResourceRegionHttpMessageConverter.class, @@ -258,33 +241,31 @@ class DefaultHttpMessageConvertersTests { @Test void registerCustomConverterInMultipartConverter() { - var converters = HttpMessageConverters.withDefaults() - .additionalMessageConverter(new CustomHttpMessageConverter()).build(); - AllEncompassingFormHttpMessageConverter multipartConverter = findMessageConverter(AllEncompassingFormHttpMessageConverter.class, converters.forServer()); + var converters = HttpMessageConverters.forServer().registerDefaults() + .customMessageConverter(new CustomHttpMessageConverter()).build(); + var multipartConverter = findMessageConverter(AllEncompassingFormHttpMessageConverter.class, converters); assertThat(multipartConverter.getPartConverters()).hasAtLeastOneElementOfType(CustomHttpMessageConverter.class); } @Test void shouldUseServerSpecificConverter() { - JacksonJsonHttpMessageConverter jacksonConverter = new JacksonJsonHttpMessageConverter(); - var converters = HttpMessageConverters.withDefaults() - .configureServer(configurer -> configurer.jsonMessageConverter(jacksonConverter)).build(); + var jacksonConverter = new JacksonJsonHttpMessageConverter(); + var converters = HttpMessageConverters.forServer().registerDefaults() + .jsonMessageConverter(jacksonConverter).build(); - JacksonJsonHttpMessageConverter customConverter = findMessageConverter(JacksonJsonHttpMessageConverter.class, converters.forServer()); + var customConverter = findMessageConverter(JacksonJsonHttpMessageConverter.class, converters); assertThat(customConverter).isEqualTo(jacksonConverter); } @Test void shouldConfigureConverter() { - CustomHttpMessageConverter customConverter = new CustomHttpMessageConverter(); - HttpMessageConverters.withDefaults() - .additionalMessageConverter(customConverter) - .configureServer(configurer -> { - configurer.configureServerMessageConverters(converter -> { - if (converter instanceof CustomHttpMessageConverter custom) { - custom.processed = true; - } - }); + var customConverter = new CustomHttpMessageConverter(); + HttpMessageConverters.forServer().registerDefaults() + .customMessageConverter(customConverter) + .configureMessageConverters(converter -> { + if (converter instanceof CustomHttpMessageConverter custom) { + custom.processed = true; + } }).build(); assertThat(customConverter.processed).isTrue(); @@ -299,32 +280,6 @@ class DefaultHttpMessageConvertersTests { .findFirst().orElseThrow(); } - - static class FilteredClassLoader extends URLClassLoader implements SmartClassLoader { - - private final Collection> hiddenClasses; - - public FilteredClassLoader(Class... hiddenClasses) { - this(java.util.Arrays.asList(hiddenClasses)); - } - - FilteredClassLoader(Collection> hiddenClasses) { - super(new URL[0], FilteredClassLoader.class.getClassLoader()); - this.hiddenClasses = hiddenClasses; - } - - @Override - protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - for (Class hiddenClass : this.hiddenClasses) { - if (hiddenClass.getName().equals(name)) { - throw new ClassNotFoundException(); - } - } - return super.loadClass(name, resolve); - } - - } - static class CustomHttpMessageConverter extends AbstractHttpMessageConverter { boolean processed = false; diff --git a/spring-web/src/test/java/org/springframework/web/client/RestClientBuilderTests.java b/spring-web/src/test/java/org/springframework/web/client/RestClientBuilderTests.java index 6dfd4ef238b..f9ba5066991 100644 --- a/spring-web/src/test/java/org/springframework/web/client/RestClientBuilderTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/RestClientBuilderTests.java @@ -41,6 +41,7 @@ import static org.assertj.core.api.Assertions.fail; /** + * Tests for {@link DefaultRestClientBuilder}. * @author Arjen Poutsma * @author Sebastien Deleuze * @author Nicklas Wiegandt @@ -139,6 +140,19 @@ public class RestClientBuilderTests { assertThatIllegalArgumentException().isThrownBy(() -> builder.messageConverters(converters)); } + @Test + void configureMessageConverters() { + StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(); + RestClient.Builder builder = RestClient.builder(); + builder.configureMessageConverters(clientBuilder -> clientBuilder.stringMessageConverter(stringConverter)); + assertThat(builder).isInstanceOf(DefaultRestClientBuilder.class); + DefaultRestClientBuilder defaultBuilder = (DefaultRestClientBuilder) builder; + + assertThat(fieldValue("messageConverters", defaultBuilder)) + .asInstanceOf(InstanceOfAssertFactories.LIST) + .containsExactly(stringConverter); + } + @Test void defaultCookieAddsCookieToDefaultCookiesMap() { RestClient.Builder builder = RestClient.builder(); diff --git a/spring-web/src/test/java/org/springframework/web/client/RestClientObservationTests.java b/spring-web/src/test/java/org/springframework/web/client/RestClientObservationTests.java index d73128aeece..cc54ae352e8 100644 --- a/spring-web/src/test/java/org/springframework/web/client/RestClientObservationTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/RestClientObservationTests.java @@ -44,7 +44,7 @@ import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.observation.ClientRequestObservationContext; import org.springframework.http.client.observation.ClientRequestObservationConvention; import org.springframework.http.client.observation.DefaultClientRequestObservationConvention; -import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.util.StreamUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -70,8 +70,6 @@ class RestClientObservationTests { private final ClientHttpResponse response = mock(); - private final HttpMessageConverter converter = mock(); - private RestClient client; @@ -84,7 +82,7 @@ class RestClientObservationTests { RestClient.Builder createBuilder() { return RestClient.builder() .baseUrl("https://example.com/base") - .messageConverters(converters -> converters.add(0, this.converter)) + .configureMessageConverters(converters -> converters.customMessageConverter(new StringHttpMessageConverter())) .requestFactory(this.requestFactory) .observationRegistry(this.observationRegistry); } @@ -203,7 +201,7 @@ class RestClientObservationTests { assertThatExceptionOfType(RestClientException.class).isThrownBy(() -> client.get().uri(url).retrieve().body(User.class)); - assertThatHttpObservation().hasLowCardinalityKeyValue("exception", "RestClientException"); + assertThatHttpObservation().hasLowCardinalityKeyValue("exception", "UnknownContentTypeException"); } @Test diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java index 5faa65d4514..084aeb46658 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java @@ -122,7 +122,7 @@ public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { } @Override - protected void configureMessageConverters(HttpMessageConverters.Builder builder) { + protected void configureMessageConverters(HttpMessageConverters.ServerBuilder builder) { this.configurers.configureMessageConverters(builder); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java index 7f6c110a5a5..98ab957ba74 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java @@ -863,9 +863,9 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv * @since 7.0 */ protected HttpMessageConverters createMessageConverters() { - HttpMessageConverters.Builder builder = HttpMessageConverters.withDefaults(); - configureMessageConverters(builder); - return builder.build(); + HttpMessageConverters.ServerBuilder serverBuilder = HttpMessageConverters.forServer().registerDefaults(); + configureMessageConverters(serverBuilder); + return serverBuilder.build(); } /** @@ -874,7 +874,7 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv * @param builder the {@code HttpMessageConverters} builder to configure * @since 7.0 */ - protected void configureMessageConverters(HttpMessageConverters.Builder builder) { + protected void configureMessageConverters(HttpMessageConverters.ServerBuilder builder) { } /** @@ -885,7 +885,7 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv * otherwise be registered by default. Also see {@link #addDefaultHttpMessageConverters} * for adding default message converters. * @param converters a list to add message converters to (initially an empty list) - * @deprecated since 7.0 in favor of {@link #configureMessageConverters(HttpMessageConverters.Builder)} + * @deprecated since 7.0 in favor of {@link #configureMessageConverters(HttpMessageConverters.ServerBuilder)} */ @Deprecated(since = "7.0", forRemoval = true) protected void configureMessageConverters(List> converters) { @@ -897,7 +897,7 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv * to be registered and then insert a custom converter through this method. * @param converters the list of configured converters to extend * @since 4.1.3 - * @deprecated since 7.0 in favor of {@link #configureMessageConverters(HttpMessageConverters.Builder)} + * @deprecated since 7.0 in favor of {@link #configureMessageConverters(HttpMessageConverters.ServerBuilder)} */ @Deprecated(since = "7.0", forRemoval = true) protected void extendMessageConverters(List> converters) { @@ -912,7 +912,7 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv @Deprecated(since = "7.0", forRemoval = true) protected final void addDefaultHttpMessageConverters(List> messageConverters) { HttpMessageConverters converters = createMessageConverters(); - converters.forServer().forEach(messageConverters::add); + converters.forEach(messageConverters::add); } /** diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java index 6dd42ce16d8..65553dd3dcf 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java @@ -177,7 +177,7 @@ public interface WebMvcConfigurer { * @param builder the builder to configure * @since 7.0 */ - default void configureMessageConverters(HttpMessageConverters.Builder builder) { + default void configureMessageConverters(HttpMessageConverters.ServerBuilder builder) { } /** @@ -195,7 +195,7 @@ public interface WebMvcConfigurer { * {@link #extendMessageConverters(java.util.List)} to modify the configured * list of message converters. * @param converters initially an empty list of converters - * @deprecated since 7.0 in favor of configuring converters on {@link #configureMessageConverters(HttpMessageConverters.Builder)} + * @deprecated since 7.0 in favor of configuring converters on {@link #configureMessageConverters(HttpMessageConverters.ServerBuilder)} */ @Deprecated(since = "7.0", forRemoval = true) default void configureMessageConverters(List> converters) { @@ -210,7 +210,7 @@ public interface WebMvcConfigurer { * the converters configured earlier will be preferred. * @param converters the list of configured converters to be extended * @since 4.1.3 - * @deprecated since 7.0 in favor of configuring converters on {@link #configureMessageConverters(HttpMessageConverters.Builder)} + * @deprecated since 7.0 in favor of configuring converters on {@link #configureMessageConverters(HttpMessageConverters.ServerBuilder)} */ @Deprecated(since = "7.0", forRemoval = true) default void extendMessageConverters(List> converters) { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerComposite.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerComposite.java index 75f97a8f83d..c6dde088f22 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerComposite.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerComposite.java @@ -142,7 +142,7 @@ class WebMvcConfigurerComposite implements WebMvcConfigurer { } @Override - public void configureMessageConverters(HttpMessageConverters.Builder builder) { + public void configureMessageConverters(HttpMessageConverters.ServerBuilder builder) { for (WebMvcConfigurer delegate : this.delegates) { delegate.configureMessageConverters(builder); } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfigurationTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfigurationTests.java index dd7d72d4b6b..152ca800735 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfigurationTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfigurationTests.java @@ -126,8 +126,8 @@ public class DelegatingWebMvcConfigurationTests { WebMvcConfigurer configurer = new WebMvcConfigurer() { @Override - public void configureMessageConverters(HttpMessageConverters.Builder builder) { - builder.additionalMessageConverter(customConverter); + public void configureMessageConverters(HttpMessageConverters.ServerBuilder builder) { + builder.customMessageConverter(customConverter); } }; webMvcConfig.setConfigurers(Collections.singletonList(configurer)); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java index e3b6190c651..75d2426aa88 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java @@ -356,11 +356,11 @@ class WebMvcConfigurationSupportExtensionTests { @Override protected HttpMessageConverters createMessageConverters() { - return HttpMessageConverters.create().jsonMessageConverter(new JacksonJsonHttpMessageConverter()).build(); + return HttpMessageConverters.forServer().jsonMessageConverter(new JacksonJsonHttpMessageConverter()).build(); } @Override - public void configureMessageConverters(HttpMessageConverters.Builder builder) { + public void configureMessageConverters(HttpMessageConverters.ServerBuilder builder) { } @Override