mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 16:39:29 +00:00
Skip Jaxb auto-detection in HttpMessageConverters for servers
Prior to this commit, `HttpMessageConverters` would consider the JAXB message converters when building `HttpMessageConverters` instances. We noticed that, on the server side, the Jakarta JAXB dependency is very common on the classpath and often brought transitively. At runtime, this converter can use significant CPU resources when checking the `canRead`/`canWrite` methods. This can happen when content types aren't strictly called out on controller endpoints. This commit changes the auto-detection mechanism in `HttpMessageConverters` to not consider the JAXB message converter for server use cases. For client use cases, we keep considering this converter as the runtime cost there is lower. Closes gh-36302
This commit is contained in:
+4
-1
@@ -31,6 +31,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
|
||||
import org.springframework.test.web.servlet.client.RestTestClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
@@ -59,7 +60,9 @@ class XmlContentTests {
|
||||
""";
|
||||
|
||||
|
||||
private final RestTestClient client = RestTestClient.bindToController(new PersonController()).build();
|
||||
private final RestTestClient client = RestTestClient.bindToController(new PersonController())
|
||||
.configureServer(server -> server.setMessageConverters(new Jaxb2RootElementHttpMessageConverter()))
|
||||
.build();
|
||||
|
||||
|
||||
@Test
|
||||
|
||||
+2
@@ -28,6 +28,7 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.test.web.Person;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
@@ -58,6 +59,7 @@ class XmlContentAssertionTests {
|
||||
|
||||
private final WebTestClient testClient =
|
||||
MockMvcWebTestClient.bindToController(new MusicController())
|
||||
.messageConverters(new Jaxb2RootElementHttpMessageConverter())
|
||||
.alwaysExpect(status().isOk())
|
||||
.alwaysExpect(content().contentType(MediaType.parseMediaType("application/xml;charset=UTF-8")))
|
||||
.configureClient()
|
||||
|
||||
+2
@@ -31,6 +31,7 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.test.web.Person;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
@@ -60,6 +61,7 @@ class XpathAssertionTests {
|
||||
|
||||
private final WebTestClient testClient =
|
||||
MockMvcWebTestClient.bindToController(new MusicController())
|
||||
.messageConverters(new Jaxb2RootElementHttpMessageConverter())
|
||||
.alwaysExpect(status().isOk())
|
||||
.alwaysExpect(content().contentType(MediaType.parseMediaType("application/xml;charset=UTF-8")))
|
||||
.configureClient()
|
||||
|
||||
+2
@@ -28,6 +28,7 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.test.web.Person;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
@@ -65,6 +66,7 @@ class XmlContentAssertionTests {
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.mockMvc = standaloneSetup(new MusicController())
|
||||
.setMessageConverters(new Jaxb2RootElementHttpMessageConverter())
|
||||
.defaultRequest(get("/").accept(MediaType.APPLICATION_XML, MediaType.parseMediaType("application/xml;charset=UTF-8")))
|
||||
.alwaysExpect(status().isOk())
|
||||
.alwaysExpect(content().contentType(MediaType.parseMediaType("application/xml;charset=UTF-8")))
|
||||
|
||||
+2
@@ -30,6 +30,7 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.test.web.Person;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
@@ -67,6 +68,7 @@ class XpathAssertionTests {
|
||||
@BeforeEach
|
||||
void setup() throws Exception {
|
||||
this.mockMvc = standaloneSetup(new MusicController())
|
||||
.setMessageConverters(new Jaxb2RootElementHttpMessageConverter())
|
||||
.defaultRequest(get("/").accept(MediaType.APPLICATION_XML, MediaType.parseMediaType("application/xml;charset=UTF-8")))
|
||||
.alwaysExpect(status().isOk())
|
||||
.alwaysExpect(content().contentType(MediaType.parseMediaType("application/xml;charset=UTF-8")))
|
||||
|
||||
+5
-1
@@ -28,6 +28,8 @@ import org.springframework.http.MediaType.APPLICATION_ATOM_XML
|
||||
import org.springframework.http.MediaType.APPLICATION_JSON
|
||||
import org.springframework.http.MediaType.APPLICATION_XML
|
||||
import org.springframework.http.MediaType.TEXT_PLAIN
|
||||
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter
|
||||
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter
|
||||
import org.springframework.test.json.JsonCompareMode
|
||||
import org.springframework.test.web.Person
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders
|
||||
@@ -52,7 +54,9 @@ import java.util.Locale
|
||||
*/
|
||||
class MockMvcExtensionsTests {
|
||||
|
||||
private val mockMvc = MockMvcBuilders.standaloneSetup(PersonController()).build()
|
||||
private val mockMvc = MockMvcBuilders.standaloneSetup(PersonController())
|
||||
.setMessageConverters(JacksonJsonHttpMessageConverter(), Jaxb2RootElementHttpMessageConverter())
|
||||
.build()
|
||||
|
||||
@Test
|
||||
fun request() {
|
||||
|
||||
+48
-17
@@ -76,39 +76,39 @@ class DefaultHttpMessageConverters implements HttpMessageConverters {
|
||||
|
||||
abstract static class DefaultBuilder {
|
||||
|
||||
private static final boolean JACKSON_PRESENT;
|
||||
static final boolean JACKSON_PRESENT;
|
||||
|
||||
private static final boolean JACKSON_2_PRESENT;
|
||||
static final boolean JACKSON_2_PRESENT;
|
||||
|
||||
private static final boolean GSON_PRESENT;
|
||||
static final boolean GSON_PRESENT;
|
||||
|
||||
private static final boolean JSONB_PRESENT;
|
||||
static final boolean JSONB_PRESENT;
|
||||
|
||||
private static final boolean KOTLIN_SERIALIZATION_JSON_PRESENT;
|
||||
static final boolean KOTLIN_SERIALIZATION_JSON_PRESENT;
|
||||
|
||||
private static final boolean JACKSON_XML_PRESENT;
|
||||
static final boolean JACKSON_XML_PRESENT;
|
||||
|
||||
private static final boolean JACKSON_2_XML_PRESENT;
|
||||
static final boolean JACKSON_2_XML_PRESENT;
|
||||
|
||||
private static final boolean JAXB_2_PRESENT;
|
||||
static final boolean JAXB_2_PRESENT;
|
||||
|
||||
private static final boolean JACKSON_SMILE_PRESENT;
|
||||
static final boolean JACKSON_SMILE_PRESENT;
|
||||
|
||||
private static final boolean JACKSON_2_SMILE_PRESENT;
|
||||
static final boolean JACKSON_2_SMILE_PRESENT;
|
||||
|
||||
private static final boolean JACKSON_CBOR_PRESENT;
|
||||
static final boolean JACKSON_CBOR_PRESENT;
|
||||
|
||||
private static final boolean JACKSON_2_CBOR_PRESENT;
|
||||
static final boolean JACKSON_2_CBOR_PRESENT;
|
||||
|
||||
private static final boolean KOTLIN_SERIALIZATION_CBOR_PRESENT;
|
||||
static final boolean KOTLIN_SERIALIZATION_CBOR_PRESENT;
|
||||
|
||||
private static final boolean JACKSON_YAML_PRESENT;
|
||||
static final boolean JACKSON_YAML_PRESENT;
|
||||
|
||||
private static final boolean JACKSON_2_YAML_PRESENT;
|
||||
static final boolean JACKSON_2_YAML_PRESENT;
|
||||
|
||||
private static final boolean KOTLIN_SERIALIZATION_PROTOBUF_PRESENT;
|
||||
static final boolean KOTLIN_SERIALIZATION_PROTOBUF_PRESENT;
|
||||
|
||||
private static final boolean ROME_PRESENT;
|
||||
static final boolean ROME_PRESENT;
|
||||
|
||||
boolean registerDefaults;
|
||||
|
||||
@@ -461,6 +461,23 @@ class DefaultHttpMessageConverters implements HttpMessageConverters {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
void detectMessageConverters() {
|
||||
super.detectMessageConverters();
|
||||
// client-specific detection behavior
|
||||
if (this.xmlConverter == null) {
|
||||
if (JACKSON_XML_PRESENT) {
|
||||
this.xmlConverter = new JacksonXmlHttpMessageConverter();
|
||||
}
|
||||
else if (JACKSON_2_XML_PRESENT) {
|
||||
this.xmlConverter = new MappingJackson2XmlHttpMessageConverter();
|
||||
}
|
||||
else if (JAXB_2_PRESENT) {
|
||||
this.xmlConverter = new Jaxb2RootElementHttpMessageConverter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpMessageConverters build() {
|
||||
if (this.registerDefaults) {
|
||||
@@ -573,6 +590,20 @@ class DefaultHttpMessageConverters implements HttpMessageConverters {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
void detectMessageConverters() {
|
||||
super.detectMessageConverters();
|
||||
// server-specific detection behavior
|
||||
if (this.xmlConverter == null) {
|
||||
if (JACKSON_XML_PRESENT) {
|
||||
this.xmlConverter = new JacksonXmlHttpMessageConverter();
|
||||
}
|
||||
else if (JACKSON_2_XML_PRESENT) {
|
||||
this.xmlConverter = new MappingJackson2XmlHttpMessageConverter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpMessageConverters build() {
|
||||
if (this.registerDefaults) {
|
||||
|
||||
Reference in New Issue
Block a user