mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-17 12:09:16 +00:00
Consider Jackson 2 in HTTP message converter auto-config
See gh-47688
This commit is contained in:
committed by
Phillip Webb
parent
e9084ddd86
commit
7b60227f41
@@ -33,8 +33,10 @@ dependencies {
|
||||
optional(project(":core:spring-boot-autoconfigure"))
|
||||
optional(project(":module:spring-boot-gson"))
|
||||
optional(project(":module:spring-boot-jackson"))
|
||||
optional(project(":module:spring-boot-jackson2"))
|
||||
optional(project(":module:spring-boot-jsonb"))
|
||||
optional(project(":module:spring-boot-kotlin-serialization"))
|
||||
optional("com.fasterxml.jackson.dataformat:jackson-dataformat-xml")
|
||||
optional("com.google.code.gson:gson")
|
||||
optional("jakarta.json.bind:jakarta.json.bind-api")
|
||||
optional("org.springframework:spring-webmvc")
|
||||
|
||||
+6
@@ -85,6 +85,12 @@ class GsonHttpMessageConvertersConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
@ConditionalOnBean(org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.class)
|
||||
static class Jackson2Available {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY,
|
||||
havingValue = "jsonb")
|
||||
static class JsonbPreferred {
|
||||
|
||||
+5
-2
@@ -51,14 +51,17 @@ import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
* @author Brian Clozel
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
@AutoConfiguration(afterName = { "org.springframework.boot.jackson.autoconfigure.JacksonAutoConfiguration",
|
||||
"org.springframework.boot.jackson2.autoconfigure.Jackson2AutoConfiguration",
|
||||
"org.springframework.boot.jsonb.autoconfigure.JsonbAutoConfiguration",
|
||||
"org.springframework.boot.gson.autoconfigure.GsonAutoConfiguration",
|
||||
"org.springframework.boot.kotlin.serialization.autoconfigure.KotlinSerializationAutoConfiguration" })
|
||||
@ConditionalOnClass(HttpMessageConverter.class)
|
||||
@Conditional(NotReactiveWebApplicationCondition.class)
|
||||
@Import({ JacksonHttpMessageConvertersConfiguration.class, GsonHttpMessageConvertersConfiguration.class,
|
||||
JsonbHttpMessageConvertersConfiguration.class, KotlinSerializationHttpMessageConvertersConfiguration.class })
|
||||
@Import({ JacksonHttpMessageConvertersConfiguration.class, Jackson2HttpMessageConvertersConfiguration.class,
|
||||
GsonHttpMessageConvertersConfiguration.class, JsonbHttpMessageConvertersConfiguration.class,
|
||||
KotlinSerializationHttpMessageConvertersConfiguration.class })
|
||||
public final class HttpMessageConvertersAutoConfiguration {
|
||||
|
||||
static final String PREFERRED_MAPPER_PROPERTY = "spring.http.converters.preferred-json-mapper";
|
||||
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.http.converter.autoconfigure;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import tools.jackson.dataformat.xml.XmlMapper;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
|
||||
|
||||
/**
|
||||
* Configuration for HTTP message converters that use Jackson 2.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @deprecated since 4.0.0 for removal in 4.2.0 in favor of Jackson 3.
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Deprecated(since = "4.0.0", forRemoval = true)
|
||||
@SuppressWarnings({ "deprecation", "removal" })
|
||||
class Jackson2HttpMessageConvertersConfiguration {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(ObjectMapper.class)
|
||||
@ConditionalOnBean(ObjectMapper.class)
|
||||
@Conditional(PreferJackson2OrJacksonUnavailableCondition.class)
|
||||
static class MappingJackson2HttpMessageConverterConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(
|
||||
ObjectMapper objectMapper) {
|
||||
return new org.springframework.http.converter.json.MappingJackson2HttpMessageConverter(objectMapper);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(XmlMapper.class)
|
||||
@ConditionalOnBean(org.springframework.http.converter.json.Jackson2ObjectMapperBuilder.class)
|
||||
protected static class MappingJackson2XmlHttpMessageConverterConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public MappingJackson2XmlHttpMessageConverter mappingJackson2XmlHttpMessageConverter(
|
||||
Jackson2ObjectMapperBuilder builder) {
|
||||
return new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class PreferJackson2OrJacksonUnavailableCondition extends AnyNestedCondition {
|
||||
|
||||
PreferJackson2OrJacksonUnavailableCondition() {
|
||||
super(ConfigurationPhase.REGISTER_BEAN);
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY,
|
||||
havingValue = "jackson2")
|
||||
static class Jackson2Preferred {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnMissingBean(JacksonJsonHttpMessageConverter.class)
|
||||
static class JacksonUnavailable {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+4
-1
@@ -66,7 +66,10 @@ class JsonbHttpMessageConvertersConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnMissingBean({ JacksonJsonHttpMessageConverter.class, GsonHttpMessageConverter.class })
|
||||
@SuppressWarnings("removal")
|
||||
@ConditionalOnMissingBean({ JacksonJsonHttpMessageConverter.class,
|
||||
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.class,
|
||||
GsonHttpMessageConverter.class })
|
||||
static class JacksonAndGsonMissing {
|
||||
|
||||
}
|
||||
|
||||
+4
-1
@@ -4,7 +4,7 @@
|
||||
"name": "spring.http.converters.preferred-json-mapper",
|
||||
"type": "java.lang.String",
|
||||
"defaultValue": "jackson",
|
||||
"description": "Preferred JSON mapper to use for HTTP message conversion. By default, auto-detected according to the environment. Supported values are 'jackson', 'gson', 'jsonb' and 'kotlin-serialization'. When other json mapping libraries are present, use a custom HttpMessageConverters bean to control the preferred mapper."
|
||||
"description": "Preferred JSON mapper to use for HTTP message conversion. By default, auto-detected according to the environment. Supported values are 'jackson', 'jackson2' (deprecated), 'gson', 'jsonb' and 'kotlin-serialization'. When other json mapping libraries are present, use a custom HttpMessageConverters bean to control the preferred mapper."
|
||||
}
|
||||
],
|
||||
"hints": [
|
||||
@@ -17,6 +17,9 @@
|
||||
{
|
||||
"value": "jackson"
|
||||
},
|
||||
{
|
||||
"value": "jackson2"
|
||||
},
|
||||
{
|
||||
"value": "jsonb"
|
||||
}
|
||||
|
||||
+97
-6
@@ -19,18 +19,20 @@ package org.springframework.boot.http.converter.autoconfigure;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.gson.Gson;
|
||||
import jakarta.json.bind.Jsonb;
|
||||
import jakarta.json.bind.JsonbBuilder;
|
||||
import kotlinx.serialization.json.Json;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import tools.jackson.databind.ObjectMapper;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
import tools.jackson.dataformat.xml.XmlMapper;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener;
|
||||
import org.springframework.boot.http.converter.autoconfigure.JacksonHttpMessageConvertersConfiguration.JacksonJsonHttpMessageConverterConfiguration;
|
||||
import org.springframework.boot.logging.LogLevel;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
@@ -77,7 +79,7 @@ class HttpMessageConvertersAutoConfigurationTests {
|
||||
@Test
|
||||
void jacksonNotAvailable() {
|
||||
this.contextRunner.run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(ObjectMapper.class);
|
||||
assertThat(context).doesNotHaveBean(JsonMapper.class);
|
||||
assertThat(context).doesNotHaveBean(JacksonJsonHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(JacksonXmlHttpMessageConverter.class);
|
||||
});
|
||||
@@ -107,6 +109,34 @@ class HttpMessageConvertersAutoConfigurationTests {
|
||||
.run(assertConverter(JacksonJsonHttpMessageConverter.class, "customJacksonMessageConverter"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated(since = "4.0.0", forRemoval = true)
|
||||
@SuppressWarnings("removal")
|
||||
void jackson2DefaultConverter() {
|
||||
this.contextRunner.withUserConfiguration(Jackson2ObjectMapperConfig.class)
|
||||
.withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO))
|
||||
.run(assertConverter(org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.class,
|
||||
"mappingJackson2HttpMessageConverter"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated(since = "4.0.0", forRemoval = true)
|
||||
@SuppressWarnings("removal")
|
||||
void jackson2ConverterWithBuilder() {
|
||||
this.contextRunner.withUserConfiguration(Jackson2ObjectMapperBuilderConfig.class)
|
||||
.run(assertConverter(org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.class,
|
||||
"mappingJackson2HttpMessageConverter"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated(since = "4.0.0", forRemoval = true)
|
||||
@SuppressWarnings("removal")
|
||||
void jackson2CustomConverter() {
|
||||
this.contextRunner.withUserConfiguration(Jackson2ObjectMapperConfig.class, Jackson2ConverterConfig.class)
|
||||
.run(assertConverter(org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.class,
|
||||
"customJacksonMessageConverter"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void gsonNotAvailable() {
|
||||
this.contextRunner.run((context) -> {
|
||||
@@ -244,8 +274,26 @@ class HttpMessageConvertersAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void gsonIsPreferredIfJacksonIsNotAvailable() {
|
||||
allOptionsRunner().withClassLoader(new FilteredClassLoader(ObjectMapper.class.getPackage().getName()))
|
||||
@SuppressWarnings("removal")
|
||||
void jackson2IsPreferredIfJacksonIsNotAvailable() {
|
||||
allOptionsRunner().withClassLoader(new FilteredClassLoader(JsonMapper.class.getPackage().getName()))
|
||||
.withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO))
|
||||
.run((context) -> {
|
||||
assertConverterBeanExists(context,
|
||||
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.class,
|
||||
"mappingJackson2HttpMessageConverter");
|
||||
assertConverterBeanRegisteredWithHttpMessageConverters(context,
|
||||
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(GsonHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(JsonbHttpMessageConverter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void gsonIsPreferredIfJacksonAndJackson2AreNotAvailable() {
|
||||
allOptionsRunner()
|
||||
.withClassLoader(new FilteredClassLoader(JsonMapper.class.getPackage().getName(),
|
||||
ObjectMapper.class.getPackage().getName()))
|
||||
.run((context) -> {
|
||||
assertConverterBeanExists(context, GsonHttpMessageConverter.class, "gsonHttpMessageConverter");
|
||||
assertConverterBeanRegisteredWithHttpMessageConverters(context, GsonHttpMessageConverter.class);
|
||||
@@ -256,8 +304,8 @@ class HttpMessageConvertersAutoConfigurationTests {
|
||||
@Test
|
||||
void jsonbIsPreferredIfJacksonAndGsonAreNotAvailable() {
|
||||
allOptionsRunner()
|
||||
.withClassLoader(new FilteredClassLoader(ObjectMapper.class.getPackage().getName(),
|
||||
Gson.class.getPackage().getName()))
|
||||
.withClassLoader(new FilteredClassLoader(JsonMapper.class.getPackage().getName(),
|
||||
ObjectMapper.class.getPackage().getName(), Gson.class.getPackage().getName()))
|
||||
.run(assertConverter(JsonbHttpMessageConverter.class, "jsonbHttpMessageConverter"));
|
||||
}
|
||||
|
||||
@@ -303,6 +351,7 @@ class HttpMessageConvertersAutoConfigurationTests {
|
||||
private ApplicationContextRunner allOptionsRunner() {
|
||||
return this.contextRunner.withBean(Gson.class)
|
||||
.withBean(JsonMapper.class)
|
||||
.withBean(ObjectMapper.class, ObjectMapper::new)
|
||||
.withBean(Jsonb.class, JsonbBuilder::create)
|
||||
.withBean(Json.class, () -> Json.Default);
|
||||
}
|
||||
@@ -403,6 +452,48 @@ class HttpMessageConvertersAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class Jackson2ObjectMapperConfig {
|
||||
|
||||
@Bean
|
||||
ObjectMapper objectMapper() {
|
||||
return new ObjectMapper();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Deprecated(since = "4.0.0", forRemoval = true)
|
||||
@SuppressWarnings("removal")
|
||||
static class Jackson2ObjectMapperBuilderConfig {
|
||||
|
||||
@Bean
|
||||
ObjectMapper objectMapper() {
|
||||
return new ObjectMapper();
|
||||
}
|
||||
|
||||
@Bean
|
||||
org.springframework.http.converter.json.Jackson2ObjectMapperBuilder builder() {
|
||||
return new org.springframework.http.converter.json.Jackson2ObjectMapperBuilder();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Deprecated(since = "4.0.0", forRemoval = true)
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@SuppressWarnings("removal")
|
||||
static class Jackson2ConverterConfig {
|
||||
|
||||
@Bean
|
||||
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter customJacksonMessageConverter(
|
||||
ObjectMapper objectMapper) {
|
||||
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter converter = new org.springframework.http.converter.json.MappingJackson2HttpMessageConverter();
|
||||
converter.setObjectMapper(objectMapper);
|
||||
return converter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class GsonConverterConfig {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user