Move spring.mvc.converters.preferred-json-mapper to spring.http
This commit deprecates spring.mvc.converters.preferred-json-mapper and replaces it with spring.http.converters.preferred-json-mapper. If both properties are specified, the latter takes precedence. Closes gh-44925
This commit is contained in:
parent
f039c73db7
commit
c604eaeb56
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2012-2025 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.autoconfigure.http;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional @Conditional} that matches based on the preferred JSON mapper. A
|
||||
* preference is expressed using the {@code spring.http.converters.preferred-json-mapper}
|
||||
* configuration property, falling back to the
|
||||
* {@code spring.mvc.converters.preferred-json-mapper} configuration property. When no
|
||||
* preference is expressed Jackson is preferred by default.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@Conditional(OnPreferredJsonMapperCondition.class)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Documented
|
||||
@interface ConditionalOnPreferredJsonMapper {
|
||||
|
||||
JsonMapper value();
|
||||
|
||||
enum JsonMapper {
|
||||
|
||||
GSON,
|
||||
|
||||
JACKSON,
|
||||
|
||||
JSONB,
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2025 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.
|
||||
|
@ -22,8 +22,8 @@ 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.boot.autoconfigure.condition.NoneNestedConditions;
|
||||
import org.springframework.boot.autoconfigure.http.ConditionalOnPreferredJsonMapper.JsonMapper;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
@ -61,8 +61,7 @@ class GsonHttpMessageConvertersConfiguration {
|
|||
super(ConfigurationPhase.REGISTER_BEAN);
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY,
|
||||
havingValue = "gson")
|
||||
@ConditionalOnPreferredJsonMapper(JsonMapper.GSON)
|
||||
static class GsonPreferred {
|
||||
|
||||
}
|
||||
|
@ -85,8 +84,7 @@ class GsonHttpMessageConvertersConfiguration {
|
|||
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY,
|
||||
havingValue = "jsonb")
|
||||
@ConditionalOnPreferredJsonMapper(JsonMapper.JSONB)
|
||||
static class JsonbPreferred {
|
||||
|
||||
}
|
||||
|
|
|
@ -66,8 +66,6 @@ import org.springframework.http.converter.StringHttpMessageConverter;
|
|||
@ImportRuntimeHints(HttpMessageConvertersAutoConfigurationRuntimeHints.class)
|
||||
public class HttpMessageConvertersAutoConfiguration {
|
||||
|
||||
static final String PREFERRED_MAPPER_PROPERTY = "spring.mvc.converters.preferred-json-mapper";
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2025 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.
|
||||
|
@ -22,7 +22,7 @@ import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
|||
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.boot.autoconfigure.http.ConditionalOnPreferredJsonMapper.JsonMapper;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
|
@ -40,8 +40,7 @@ class JacksonHttpMessageConvertersConfiguration {
|
|||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(ObjectMapper.class)
|
||||
@ConditionalOnBean(ObjectMapper.class)
|
||||
@ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY,
|
||||
havingValue = "jackson", matchIfMissing = true)
|
||||
@ConditionalOnPreferredJsonMapper(JsonMapper.JACKSON)
|
||||
static class MappingJackson2HttpMessageConverterConfiguration {
|
||||
|
||||
@Bean
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2025 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.
|
||||
|
@ -22,7 +22,7 @@ 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.boot.autoconfigure.http.ConditionalOnPreferredJsonMapper.JsonMapper;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
@ -60,8 +60,7 @@ class JsonbHttpMessageConvertersConfiguration {
|
|||
super(ConfigurationPhase.REGISTER_BEAN);
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY,
|
||||
havingValue = "jsonb")
|
||||
@ConditionalOnPreferredJsonMapper(JsonMapper.JSONB)
|
||||
static class JsonbPreferred {
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2012-2025 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.autoconfigure.http;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.boot.autoconfigure.http.ConditionalOnPreferredJsonMapper.JsonMapper;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
|
||||
/**
|
||||
* {@link SpringBootCondition} for
|
||||
* {@link ConditionalOnPreferredJsonMapper @ConditionalOnPreferredJsonMapper}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class OnPreferredJsonMapperCondition extends SpringBootCondition {
|
||||
|
||||
private static final String PREFERRED_MAPPER_PROPERTY = "spring.http.converters.preferred-json-mapper";
|
||||
|
||||
@Deprecated(since = "3.5.0", forRemoval = true)
|
||||
private static final String DEPRECATED_PREFERRED_MAPPER_PROPERTY = "spring.mvc.converters.preferred-json-mapper";
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
JsonMapper conditionMapper = metadata.getAnnotations()
|
||||
.get(ConditionalOnPreferredJsonMapper.class)
|
||||
.getEnum("value", JsonMapper.class);
|
||||
ConditionOutcome outcome = getMatchOutcome(context.getEnvironment(), PREFERRED_MAPPER_PROPERTY,
|
||||
conditionMapper);
|
||||
if (outcome != null) {
|
||||
return outcome;
|
||||
}
|
||||
outcome = getMatchOutcome(context.getEnvironment(), DEPRECATED_PREFERRED_MAPPER_PROPERTY, conditionMapper);
|
||||
if (outcome != null) {
|
||||
return outcome;
|
||||
}
|
||||
ConditionMessage message = ConditionMessage
|
||||
.forCondition(ConditionalOnPreferredJsonMapper.class, conditionMapper.name())
|
||||
.because("no property was configured and Jackson is the default");
|
||||
return (conditionMapper == JsonMapper.JACKSON) ? ConditionOutcome.match(message)
|
||||
: ConditionOutcome.noMatch(message);
|
||||
}
|
||||
|
||||
private ConditionOutcome getMatchOutcome(Environment environment, String key, JsonMapper conditionMapper) {
|
||||
String property = environment.getProperty(key);
|
||||
if (property == null) {
|
||||
return null;
|
||||
}
|
||||
JsonMapper configuredMapper = JsonMapper.valueOf(property.toUpperCase(Locale.ROOT));
|
||||
ConditionMessage message = ConditionMessage
|
||||
.forCondition(ConditionalOnPreferredJsonMapper.class, configuredMapper.name())
|
||||
.because("property '%s' had the value '%s'".formatted(key, property));
|
||||
return (configuredMapper == conditionMapper) ? ConditionOutcome.match(message)
|
||||
: ConditionOutcome.noMatch(message);
|
||||
}
|
||||
|
||||
}
|
|
@ -1615,11 +1615,8 @@
|
|||
{
|
||||
"name": "spring.http.converters.preferred-json-mapper",
|
||||
"type": "java.lang.String",
|
||||
"description": "Preferred JSON mapper to use for HTTP message conversion. By default, auto-detected according to the environment.",
|
||||
"deprecation": {
|
||||
"replacement": "spring.mvc.converters.preferred-json-mapper",
|
||||
"level": "error"
|
||||
}
|
||||
"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', and 'jsonb'. When other json mapping libraries (such as kotlinx.serialization) are present, use a custom HttpMessageConverters bean to control the preferred mapper."
|
||||
},
|
||||
{
|
||||
"name": "spring.http.encoding.charset",
|
||||
|
@ -2107,7 +2104,11 @@
|
|||
"name": "spring.mvc.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', and 'jsonb'. When other json mapping libraries (such as kotlinx.serialization) 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', 'gson', and 'jsonb'. When other json mapping libraries (such as kotlinx.serialization) are present, use a custom HttpMessageConverters bean to control the preferred mapper.",
|
||||
"deprecation": {
|
||||
"replacement": "spring.http.converters.preferred-json-mapper",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "spring.mvc.date-format",
|
||||
|
|
|
@ -32,7 +32,9 @@ import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConf
|
|||
import org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration.MappingJackson2HttpMessageConverterConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
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;
|
||||
|
@ -130,12 +132,41 @@ class HttpMessageConvertersAutoConfigurationTests {
|
|||
|
||||
@Test
|
||||
void gsonCanBePreferred() {
|
||||
allOptionsRunner().withPropertyValues("spring.mvc.converters.preferred-json-mapper:gson").run((context) -> {
|
||||
assertConverterBeanExists(context, GsonHttpMessageConverter.class, "gsonHttpMessageConverter");
|
||||
assertConverterBeanRegisteredWithHttpMessageConverters(context, GsonHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(JsonbHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(MappingJackson2HttpMessageConverter.class);
|
||||
});
|
||||
allOptionsRunner().withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO))
|
||||
.withPropertyValues("spring.http.converters.preferred-json-mapper:gson")
|
||||
.run((context) -> {
|
||||
assertConverterBeanExists(context, GsonHttpMessageConverter.class, "gsonHttpMessageConverter");
|
||||
assertConverterBeanRegisteredWithHttpMessageConverters(context, GsonHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(JsonbHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(MappingJackson2HttpMessageConverter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated(since = "3.5.0", forRemoval = true)
|
||||
void gsonCanBePreferredWithDeprecatedProperty() {
|
||||
allOptionsRunner().withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO))
|
||||
.withPropertyValues("spring.mvc.converters.preferred-json-mapper:gson")
|
||||
.run((context) -> {
|
||||
assertConverterBeanExists(context, GsonHttpMessageConverter.class, "gsonHttpMessageConverter");
|
||||
assertConverterBeanRegisteredWithHttpMessageConverters(context, GsonHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(JsonbHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(MappingJackson2HttpMessageConverter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated(since = "3.5.0", forRemoval = true)
|
||||
void gsonCanBePreferredWithNonDeprecatedPropertyTakingPrecedence() {
|
||||
allOptionsRunner().withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO))
|
||||
.withPropertyValues("spring.http.converters.preferred-json-mapper:gson",
|
||||
"spring.mvc.converters.preferred-json-mapper:jackson")
|
||||
.run((context) -> {
|
||||
assertConverterBeanExists(context, GsonHttpMessageConverter.class, "gsonHttpMessageConverter");
|
||||
assertConverterBeanRegisteredWithHttpMessageConverters(context, GsonHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(JsonbHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(MappingJackson2HttpMessageConverter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -169,6 +200,31 @@ class HttpMessageConvertersAutoConfigurationTests {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated(since = "3.5.0", forRemoval = true)
|
||||
void jsonbCanBePreferredWithDeprecatedProperty() {
|
||||
allOptionsRunner().withPropertyValues("spring.http.converters.preferred-json-mapper:jsonb").run((context) -> {
|
||||
assertConverterBeanExists(context, JsonbHttpMessageConverter.class, "jsonbHttpMessageConverter");
|
||||
assertConverterBeanRegisteredWithHttpMessageConverters(context, JsonbHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(GsonHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(MappingJackson2HttpMessageConverter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated(since = "3.5.0", forRemoval = true)
|
||||
void jsonbCanBePreferredWithNonDeprecatedPropertyTakingPrecedence() {
|
||||
allOptionsRunner()
|
||||
.withPropertyValues("spring.http.converters.preferred-json-mapper:jsonb",
|
||||
"spring.mvc.converters.preferred-json-mapper:gson")
|
||||
.run((context) -> {
|
||||
assertConverterBeanExists(context, JsonbHttpMessageConverter.class, "jsonbHttpMessageConverter");
|
||||
assertConverterBeanRegisteredWithHttpMessageConverters(context, JsonbHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(GsonHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(MappingJackson2HttpMessageConverter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void stringDefaultConverter() {
|
||||
this.contextRunner.run(assertConverter(StringHttpMessageConverter.class, "stringHttpMessageConverter"));
|
||||
|
|
Loading…
Reference in New Issue