Skip to content

Commit 28f9adf

Browse files
committed
Simplify media files detection in WebMvcConfigurationSupport
Prior to this commit, `WebMvcConfigurationSupport` would configure file extensions/media types registrations based on classpath detection. Since gh-33894, the detection of message converters is located in a single place, `HttpMessageConverters`. This commit updates the `WebMvcConfigurationSupport` to use the actual message converters configured to decide which file extensions should be set up for content negotiation. See gh-33894
1 parent 02ff681 commit 28f9adf

File tree

3 files changed

+38
-55
lines changed

3 files changed

+38
-55
lines changed

spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java

Lines changed: 19 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@
1616

1717
package org.springframework.web.servlet.config.annotation;
1818

19+
import java.nio.charset.StandardCharsets;
1920
import java.util.ArrayList;
2021
import java.util.HashMap;
2122
import java.util.List;
2223
import java.util.Locale;
2324
import java.util.Map;
25+
import java.util.Set;
26+
import java.util.stream.Collectors;
2427

2528
import jakarta.servlet.ServletContext;
2629
import org.jspecify.annotations.Nullable;
@@ -177,63 +180,19 @@
177180
*/
178181
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
179182

180-
private static final boolean romePresent;
181-
182-
private static final boolean jaxb2Present;
183-
184183
private static final boolean jacksonPresent;
185184

186185
private static final boolean jackson2Present;
187186

188-
private static final boolean jacksonXmlPresent;
189-
190-
private static final boolean jackson2XmlPresent;
191-
192-
private static final boolean jacksonSmilePresent;
193-
194-
private static final boolean jackson2SmilePresent;
195-
196-
private static final boolean jacksonCborPresent;
197-
198-
private static final boolean jackson2CborPresent;
199-
200-
private static final boolean jacksonYamlPresent;
201-
202-
private static final boolean jackson2YamlPresent;
203-
204-
private static final boolean gsonPresent;
205-
206-
private static final boolean jsonbPresent;
207-
208187
private static final boolean kotlinSerializationPresent;
209188

210-
private static final boolean kotlinSerializationCborPresent;
211-
212-
private static final boolean kotlinSerializationJsonPresent;
213-
214-
private static final boolean kotlinSerializationProtobufPresent;
215189

216190
static {
217191
ClassLoader classLoader = WebMvcConfigurationSupport.class.getClassLoader();
218-
romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);
219-
jaxb2Present = ClassUtils.isPresent("jakarta.xml.bind.Binder", classLoader);
220192
jacksonPresent = ClassUtils.isPresent("tools.jackson.databind.ObjectMapper", classLoader);
221193
jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
222194
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
223-
jacksonXmlPresent = jacksonPresent && ClassUtils.isPresent("tools.jackson.dataformat.xml.XmlMapper", classLoader);
224-
jackson2XmlPresent = jackson2Present && ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
225-
jacksonSmilePresent = jacksonPresent && ClassUtils.isPresent("tools.jackson.dataformat.smile.SmileMapper", classLoader);
226-
jackson2SmilePresent = jackson2Present && ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
227-
jacksonCborPresent = jacksonPresent && ClassUtils.isPresent("tools.jackson.dataformat.cbor.CBORMapper", classLoader);
228-
jackson2CborPresent = jackson2Present && ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);
229-
jacksonYamlPresent = jacksonPresent && ClassUtils.isPresent("tools.jackson.dataformat.yaml.YAMLMapper", classLoader);
230-
jackson2YamlPresent = jackson2Present && ClassUtils.isPresent("com.fasterxml.jackson.dataformat.yaml.YAMLFactory", classLoader);
231-
gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
232-
jsonbPresent = ClassUtils.isPresent("jakarta.json.bind.Jsonb", classLoader);
233195
kotlinSerializationPresent = ClassUtils.isPresent("kotlinx.serialization.Serializable", classLoader);
234-
kotlinSerializationCborPresent = kotlinSerializationPresent && ClassUtils.isPresent("kotlinx.serialization.cbor.Cbor", classLoader);
235-
kotlinSerializationJsonPresent = kotlinSerializationPresent && ClassUtils.isPresent("kotlinx.serialization.json.Json", classLoader);
236-
kotlinSerializationProtobufPresent = kotlinSerializationPresent && ClassUtils.isPresent("kotlinx.serialization.protobuf.ProtoBuf", classLoader);
237196
}
238197

239198

@@ -444,23 +403,32 @@ public ContentNegotiationManager mvcContentNegotiationManager() {
444403

445404
protected Map<String, MediaType> getDefaultMediaTypes() {
446405
Map<String, MediaType> map = new HashMap<>(4);
447-
if (romePresent) {
406+
List<HttpMessageConverter<?>> messageConverters = getMessageConverters();
407+
Set<MediaType> supportedMediaTypes = messageConverters.stream()
408+
.flatMap(converter -> converter.getSupportedMediaTypes().stream())
409+
.collect(Collectors.toSet());
410+
if (supportedMediaTypes.contains(MediaType.APPLICATION_ATOM_XML)) {
448411
map.put("atom", MediaType.APPLICATION_ATOM_XML);
412+
}
413+
if (supportedMediaTypes.contains(MediaType.APPLICATION_RSS_XML)) {
449414
map.put("rss", MediaType.APPLICATION_RSS_XML);
450415
}
451-
if (jaxb2Present || jacksonXmlPresent || jackson2XmlPresent) {
416+
MediaType xmlUtf8MediaType = new MediaType("application", "xml", StandardCharsets.UTF_8);
417+
if (supportedMediaTypes.contains(MediaType.APPLICATION_XML) ||
418+
supportedMediaTypes.contains(xmlUtf8MediaType)) {
452419
map.put("xml", MediaType.APPLICATION_XML);
453420
}
454-
if (jacksonPresent || jackson2Present || gsonPresent || jsonbPresent || kotlinSerializationJsonPresent) {
421+
if (supportedMediaTypes.contains(MediaType.APPLICATION_JSON)) {
455422
map.put("json", MediaType.APPLICATION_JSON);
456423
}
457-
if (jacksonSmilePresent || jackson2SmilePresent) {
458-
map.put("smile", MediaType.valueOf("application/x-jackson-smile"));
424+
MediaType smileMediaType = new MediaType("application", "x-jackson-smile");
425+
if (supportedMediaTypes.contains(smileMediaType)) {
426+
map.put("smile", smileMediaType);
459427
}
460-
if (jacksonCborPresent || jackson2CborPresent || kotlinSerializationCborPresent) {
428+
if (supportedMediaTypes.contains(MediaType.APPLICATION_CBOR)) {
461429
map.put("cbor", MediaType.APPLICATION_CBOR);
462430
}
463-
if (jacksonYamlPresent || jackson2YamlPresent) {
431+
if (supportedMediaTypes.contains(MediaType.APPLICATION_ATOM_XML)) {
464432
map.put("yaml", MediaType.APPLICATION_YAML);
465433
}
466434
return map;

spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@
9191
import static org.assertj.core.api.Assertions.assertThat;
9292
import static org.mockito.Mockito.mock;
9393
import static org.springframework.http.MediaType.APPLICATION_JSON;
94-
import static org.springframework.http.MediaType.APPLICATION_XML;
9594

9695
/**
9796
* A test fixture with a subclass of {@link WebMvcConfigurationSupport} that also
@@ -276,9 +275,6 @@ public void contentNegotiation() throws Exception {
276275
ContentNegotiationManager manager = mapping.getContentNegotiationManager();
277276
assertThat(manager.resolveMediaTypes(webRequest)).isEqualTo(Collections.singletonList(APPLICATION_JSON));
278277

279-
request.setParameter("f", "xml");
280-
assertThat(manager.resolveMediaTypes(webRequest)).isEqualTo(Collections.singletonList(APPLICATION_XML));
281-
282278
SimpleUrlHandlerMapping handlerMapping = (SimpleUrlHandlerMapping) this.config.resourceHandlerMapping(
283279
this.config.mvcContentNegotiationManager(), this.config.mvcConversionService(),
284280
this.config.mvcResourceUrlProvider());

spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportTests.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import org.springframework.format.support.FormattingConversionService;
4646
import org.springframework.http.HttpEntity;
4747
import org.springframework.http.HttpStatus;
48+
import org.springframework.http.MediaType;
4849
import org.springframework.http.converter.AbstractJacksonHttpMessageConverter;
4950
import org.springframework.http.converter.HttpMessageConverter;
5051
import org.springframework.http.converter.xml.JacksonXmlHttpMessageConverter;
@@ -53,6 +54,7 @@
5354
import org.springframework.util.PathMatcher;
5455
import org.springframework.validation.Validator;
5556
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
57+
import org.springframework.web.accept.ContentNegotiationManager;
5658
import org.springframework.web.bind.annotation.PathVariable;
5759
import org.springframework.web.bind.annotation.RequestMapping;
5860
import org.springframework.web.bind.annotation.ResponseStatus;
@@ -213,6 +215,23 @@ void requestMappingHandlerAdapter() {
213215
assertThat(bodyAdvice.get(3).getClass()).isEqualTo(KotlinResponseBodyAdvice.class);
214216
}
215217

218+
@Test
219+
void contentNegotiationManager() {
220+
ApplicationContext context = initContext(WebConfig.class);
221+
ContentNegotiationManager contentNegotiation = context.getBean(ContentNegotiationManager.class);
222+
Map<String, MediaType> mediaTypeMappings = contentNegotiation.getMediaTypeMappings();
223+
224+
assertThat(mediaTypeMappings)
225+
.containsEntry("atom", MediaType.APPLICATION_ATOM_XML)
226+
.containsEntry("rss", MediaType.APPLICATION_RSS_XML)
227+
.containsEntry("rss", MediaType.APPLICATION_RSS_XML)
228+
.containsEntry("xml", MediaType.APPLICATION_XML)
229+
.containsEntry("json", MediaType.APPLICATION_JSON)
230+
.containsEntry("smile", MediaType.valueOf("application/x-jackson-smile"))
231+
.containsEntry("cbor", MediaType.APPLICATION_CBOR)
232+
.containsEntry("yaml", MediaType.APPLICATION_YAML);
233+
}
234+
216235
@Test
217236
void uriComponentsContributor() {
218237
ApplicationContext context = initContext(WebConfig.class);

0 commit comments

Comments
 (0)