Browse Source

Consider Jackson 2 in HTTP codec auto-config

See gh-47688
pull/47694/head
Andy Wilkinson 4 months ago committed by Phillip Webb
parent
commit
eb5cb2d4e1
  1. 1
      module/spring-boot-http-codec/build.gradle
  2. 53
      module/spring-boot-http-codec/src/main/java/org/springframework/boot/http/codec/autoconfigure/CodecsAutoConfiguration.java
  3. 30
      module/spring-boot-http-codec/src/main/resources/META-INF/additional-spring-configuration-metadata.json
  4. 45
      module/spring-boot-http-codec/src/test/java/org/springframework/boot/http/codec/autoconfigure/CodecsAutoConfigurationTests.java

1
module/spring-boot-http-codec/build.gradle

@ -31,6 +31,7 @@ dependencies { @@ -31,6 +31,7 @@ dependencies {
optional(project(":core:spring-boot-autoconfigure"))
optional(project(":core:spring-boot-test"))
optional(project(":module:spring-boot-jackson"))
optional(project(":module:spring-boot-jackson2"))
optional("org.springframework:spring-webflux")
testImplementation(project(":core:spring-boot-test"))

53
module/spring-boot-http-codec/src/main/java/org/springframework/boot/http/codec/autoconfigure/CodecsAutoConfiguration.java

@ -16,18 +16,22 @@ @@ -16,18 +16,22 @@
package org.springframework.boot.http.codec.autoconfigure;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.jspecify.annotations.Nullable;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.json.JsonMapper;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
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.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.http.codec.CodecCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
@ -43,14 +47,17 @@ import org.springframework.web.reactive.function.client.WebClient; @@ -43,14 +47,17 @@ import org.springframework.web.reactive.function.client.WebClient;
* {@link org.springframework.core.codec.Decoder Decoders}.
*
* @author Brian Clozel
* @since 2.0.0
* @since 4.0.0
*/
@AutoConfiguration(afterName = "org.springframework.boot.jackson.autoconfigure.JacksonAutoConfiguration")
@AutoConfiguration(afterName = { "org.springframework.boot.jackson.autoconfigure.JacksonAutoConfiguration",
"org.springframework.boot.jackson2.autoconfigure.Jackson2AutoConfiguration" })
@ConditionalOnClass({ CodecConfigurer.class, WebClient.class })
public final class CodecsAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ObjectMapper.class)
@ConditionalOnClass(JsonMapper.class)
@ConditionalOnProperty(name = "spring.http.codecs.preferred-json-mapper", havingValue = "jackson",
matchIfMissing = true)
static class JacksonJsonCodecConfiguration {
@Bean
@ -66,6 +73,26 @@ public final class CodecsAutoConfiguration { @@ -66,6 +73,26 @@ public final class CodecsAutoConfiguration {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ObjectMapper.class)
@Conditional(NoJacksonOrJackson2Preferred.class)
@Deprecated(since = "4.0.0", forRemoval = true)
@SuppressWarnings("removal")
static class Jackson2JsonCodecConfiguration {
@Bean
@Order(0)
@ConditionalOnBean(ObjectMapper.class)
CodecCustomizer jackson2CodecCustomizer(ObjectMapper objectMapper) {
return (configurer) -> {
CodecConfigurer.DefaultCodecs defaults = configurer.defaultCodecs();
defaults.jacksonJsonDecoder(new org.springframework.http.codec.json.Jackson2JsonDecoder(objectMapper));
defaults.jacksonJsonEncoder(new org.springframework.http.codec.json.Jackson2JsonEncoder(objectMapper));
};
}
}
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(HttpCodecsProperties.class)
static class DefaultCodecsConfiguration {
@ -104,4 +131,22 @@ public final class CodecsAutoConfiguration { @@ -104,4 +131,22 @@ public final class CodecsAutoConfiguration {
}
static class NoJacksonOrJackson2Preferred extends AnyNestedCondition {
NoJacksonOrJackson2Preferred() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@ConditionalOnMissingClass("tools.jackson.databind.json.JsonMapper")
static class NoJackson {
}
@ConditionalOnProperty(name = "spring.http.codecs.preferred-json-mapper", havingValue = "jackson2")
static class Jackson2Preferred {
}
}
}

30
module/spring-boot-http-codec/src/main/resources/META-INF/additional-spring-configuration-metadata.json

@ -1,4 +1,28 @@ @@ -1,4 +1,28 @@
{
"groups": [],
"properties": []
{
"properties": [
{
"name": "spring.http.codecs.preferred-json-mapper",
"type": "java.lang.String",
"defaultValue": "jackson",
"description": "Preferred JSON mapper to use for HTTP encoding and decoding. By default, auto-detected according to the environment. Supported values are 'jackson' and 'jackson2' (deprecated)."
}
],
"hints": [
{
"name": "spring.http.codecs.preferred-json-mapper",
"values": [
{
"value": "jackson"
},
{
"value": "jackson2"
}
],
"providers": [
{
"name": "any"
}
]
}
]
}

45
module/spring-boot-http-codec/src/test/java/org/springframework/boot/http/codec/autoconfigure/CodecsAutoConfigurationTests.java

@ -18,11 +18,13 @@ package org.springframework.boot.http.codec.autoconfigure; @@ -18,11 +18,13 @@ package org.springframework.boot.http.codec.autoconfigure;
import java.util.List;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import tools.jackson.databind.json.JsonMapper;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.http.codec.CodecCustomizer;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
@ -72,7 +74,7 @@ class CodecsAutoConfigurationTests { @@ -72,7 +74,7 @@ class CodecsAutoConfigurationTests {
}
@Test
void jacksonCodecCustomizerBacksOffWhenThereIsNoObjectMapper() {
void jacksonCodecCustomizerBacksOffWhenThereIsNoJsonMapper() {
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean("jacksonCodecCustomizer"));
}
@ -82,6 +84,37 @@ class CodecsAutoConfigurationTests { @@ -82,6 +84,37 @@ class CodecsAutoConfigurationTests {
.run((context) -> assertThat(context).hasBean("jacksonCodecCustomizer"));
}
@Test
@Deprecated(since = "4.0.0", forRemoval = true)
void jacksonCodecCustomizerBacksOffWhenJackson2IsPreferred() {
this.contextRunner.withUserConfiguration(JsonMapperConfiguration.class)
.withPropertyValues("spring.http.codecs.preferred-json-mapper=jackson2")
.run((context) -> assertThat(context).doesNotHaveBean("jacksonCodecCustomizer"));
}
@Test
@Deprecated(since = "4.0.0", forRemoval = true)
void jackson2CodecCustomizerIsAutoConfiguredWhenObjectMapperIsPresentAndJackson2IsPreferred() {
this.contextRunner.withUserConfiguration(ObjectMapperConfiguration.class)
.withPropertyValues("spring.http.codecs.preferred-json-mapper=jackson2")
.run((context) -> assertThat(context).hasBean("jackson2CodecCustomizer"));
}
@Test
@Deprecated(since = "4.0.0", forRemoval = true)
void jackson2CodecCustomizerIsAutoConfiguredWhenObjectMapperIsPresentAndJacksonIsMissing() {
this.contextRunner.withUserConfiguration(ObjectMapperConfiguration.class)
.withClassLoader(new FilteredClassLoader(JsonMapper.class.getPackage().getName()))
.run((context) -> assertThat(context).hasBean("jackson2CodecCustomizer"));
}
@Test
@Deprecated(since = "4.0.0", forRemoval = true)
void jackson2CodecCustomizerBacksOffWhenJackson2IsPreferredButThereIsNoObjectMapper() {
this.contextRunner.withPropertyValues("spring.http.codecs.preferred-json-mapper=jackson2")
.run((context) -> assertThat(context).doesNotHaveBean("jackson2CodecCustomizer"));
}
@Test
void userProvidedCustomizerCanOverrideJacksonCodecCustomizer() {
this.contextRunner.withUserConfiguration(JsonMapperConfiguration.class, CodecCustomizerConfiguration.class)
@ -116,6 +149,16 @@ class CodecsAutoConfigurationTests { @@ -116,6 +149,16 @@ class CodecsAutoConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
static class ObjectMapperConfiguration {
@Bean
ObjectMapper objectMapper() {
return new ObjectMapper();
}
}
@Configuration(proxyBeanMethods = false)
static class CodecCustomizerConfiguration {

Loading…
Cancel
Save