mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-10-04 00:09:01 +00:00
Revisit Kotlin Serialization integration
This commit removes the "kotlin-serialization" option from the `spring.http.converters.preferred-json-mapper` and configures the kotlin serialization http message converter ahead of the preferred JSON converter. This effectively makes Kotlin Serialization a converter that is considered first for JSON support, and then Jackson/Jsonb/Gson is considered as fallback. Closes gh-47178
This commit is contained in:
+6
-12
@@ -42,7 +42,7 @@ class GsonHttpMessageConvertersConfiguration {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnBean(Gson.class)
|
||||
@Conditional(PreferGsonOrOtherJsonLibraryUnavailableCondition.class)
|
||||
@Conditional(PreferGsonOrJacksonAndJsonbUnavailableCondition.class)
|
||||
static class GsonHttpMessageConverterConfiguration {
|
||||
|
||||
@Bean
|
||||
@@ -55,9 +55,9 @@ class GsonHttpMessageConvertersConfiguration {
|
||||
|
||||
}
|
||||
|
||||
private static class PreferGsonOrOtherJsonLibraryUnavailableCondition extends AnyNestedCondition {
|
||||
private static class PreferGsonOrJacksonAndJsonbUnavailableCondition extends AnyNestedCondition {
|
||||
|
||||
PreferGsonOrOtherJsonLibraryUnavailableCondition() {
|
||||
PreferGsonOrJacksonAndJsonbUnavailableCondition() {
|
||||
super(ConfigurationPhase.REGISTER_BEAN);
|
||||
}
|
||||
|
||||
@@ -67,16 +67,16 @@ class GsonHttpMessageConvertersConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Conditional(OtherJsonLibrariesUnavailableCondition.class)
|
||||
@Conditional(JacksonAndJsonbUnavailableCondition.class)
|
||||
static class JacksonJsonbUnavailable {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class OtherJsonLibrariesUnavailableCondition extends NoneNestedConditions {
|
||||
private static class JacksonAndJsonbUnavailableCondition extends NoneNestedConditions {
|
||||
|
||||
OtherJsonLibrariesUnavailableCondition() {
|
||||
JacksonAndJsonbUnavailableCondition() {
|
||||
super(ConfigurationPhase.REGISTER_BEAN);
|
||||
}
|
||||
|
||||
@@ -91,12 +91,6 @@ class GsonHttpMessageConvertersConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY,
|
||||
havingValue = "kotlin-serialization")
|
||||
static class KotlinSerializationPreferred {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-30
@@ -23,7 +23,6 @@ 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.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -42,7 +41,7 @@ class JsonbHttpMessageConvertersConfiguration {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnBean(Jsonb.class)
|
||||
@Conditional(PreferJsonbOrOtherJsonLibrariesMissingCondition.class)
|
||||
@Conditional(PreferJsonbOrMissingJacksonAndGsonCondition.class)
|
||||
static class JsonbHttpMessageConverterConfiguration {
|
||||
|
||||
@Bean
|
||||
@@ -55,9 +54,9 @@ class JsonbHttpMessageConvertersConfiguration {
|
||||
|
||||
}
|
||||
|
||||
private static class PreferJsonbOrOtherJsonLibrariesMissingCondition extends AnyNestedCondition {
|
||||
private static class PreferJsonbOrMissingJacksonAndGsonCondition extends AnyNestedCondition {
|
||||
|
||||
PreferJsonbOrOtherJsonLibrariesMissingCondition() {
|
||||
PreferJsonbOrMissingJacksonAndGsonCondition() {
|
||||
super(ConfigurationPhase.REGISTER_BEAN);
|
||||
}
|
||||
|
||||
@@ -67,32 +66,8 @@ class JsonbHttpMessageConvertersConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Conditional(OtherJsonLibrariesMissingMissingCondition.class)
|
||||
static class OtherJsonLibrariesMissingMissing {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class OtherJsonLibrariesMissingMissingCondition extends NoneNestedConditions {
|
||||
|
||||
OtherJsonLibrariesMissingMissingCondition() {
|
||||
super(ConfigurationPhase.REGISTER_BEAN);
|
||||
}
|
||||
|
||||
@ConditionalOnBean(JacksonJsonHttpMessageConverter.class)
|
||||
static class JacksonAvailable {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnBean(GsonHttpMessageConverter.class)
|
||||
static class GsonAvailable {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY,
|
||||
havingValue = "kotlin-serialization")
|
||||
static class KotlinPreferred {
|
||||
@ConditionalOnMissingBean({ JacksonJsonHttpMessageConverter.class, GsonHttpMessageConverter.class })
|
||||
static class JacksonAndGsonMissing {
|
||||
|
||||
}
|
||||
|
||||
|
||||
+10
-38
@@ -16,61 +16,33 @@
|
||||
|
||||
package org.springframework.boot.http.converter.autoconfigure;
|
||||
|
||||
import kotlinx.serialization.Serializable;
|
||||
import kotlinx.serialization.json.Json;
|
||||
|
||||
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.GsonHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.JsonbHttpMessageConverter;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.converter.json.KotlinSerializationJsonHttpMessageConverter;
|
||||
|
||||
/**
|
||||
* Configuration for HTTP message converters that use Kotlin Serialization.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Dmitry Sulman
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(Json.class)
|
||||
@ConditionalOnClass({ Serializable.class, Json.class })
|
||||
@ConditionalOnBean(Json.class)
|
||||
class KotlinSerializationHttpMessageConvertersConfiguration {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnBean(Json.class)
|
||||
@Conditional(PreferKotlinSerializationOrOtherJsonLibrariesUnavailableCondition.class)
|
||||
static class KotlinSerializationHttpMessageConverterConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
KotlinSerializationJsonHttpMessageConverter kotlinSerializationJsonHttpMessageConverter(Json json) {
|
||||
return new KotlinSerializationJsonHttpMessageConverter(json);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class PreferKotlinSerializationOrOtherJsonLibrariesUnavailableCondition extends AnyNestedCondition {
|
||||
|
||||
PreferKotlinSerializationOrOtherJsonLibrariesUnavailableCondition() {
|
||||
super(ConfigurationPhase.REGISTER_BEAN);
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY,
|
||||
havingValue = "kotlin-serialization")
|
||||
static class KotlinSerializationPreferred {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnMissingBean({ JacksonJsonHttpMessageConverter.class, JsonbHttpMessageConverter.class,
|
||||
GsonHttpMessageConverter.class })
|
||||
static class OtherJsonLibrariesUnavailable {
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@Order(-10) // configured ahead of JSON mappers
|
||||
KotlinSerializationJsonHttpMessageConverter kotlinSerializationJsonHttpMessageConverter(Json json) {
|
||||
return new KotlinSerializationJsonHttpMessageConverter(json);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-3
@@ -19,9 +19,6 @@
|
||||
},
|
||||
{
|
||||
"value": "jsonb"
|
||||
},
|
||||
{
|
||||
"value": "kotlin-serialization"
|
||||
}
|
||||
],
|
||||
"providers": [
|
||||
|
||||
+19
-15
@@ -17,6 +17,7 @@
|
||||
package org.springframework.boot.http.converter.autoconfigure;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import jakarta.json.bind.Jsonb;
|
||||
@@ -131,7 +132,6 @@ class HttpMessageConvertersAutoConfigurationTests {
|
||||
assertConverterBeanRegisteredWithHttpMessageConverters(context, GsonHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(JsonbHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(JacksonJsonHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(KotlinSerializationJsonHttpMessageConverter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -163,7 +163,6 @@ class HttpMessageConvertersAutoConfigurationTests {
|
||||
assertConverterBeanRegisteredWithHttpMessageConverters(context, JsonbHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(GsonHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(JacksonJsonHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(KotlinSerializationJsonHttpMessageConverter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -184,17 +183,15 @@ class HttpMessageConvertersAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void kotlinSerializationCanBePreferred() {
|
||||
allOptionsRunner().withPropertyValues("spring.http.converters.preferred-json-mapper:kotlin-serialization")
|
||||
.run((context) -> {
|
||||
assertConverterBeanExists(context, KotlinSerializationJsonHttpMessageConverter.class,
|
||||
"kotlinSerializationJsonHttpMessageConverter");
|
||||
assertConverterBeanRegisteredWithHttpMessageConverters(context,
|
||||
KotlinSerializationJsonHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(GsonHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(JsonbHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(JacksonJsonHttpMessageConverter.class);
|
||||
});
|
||||
void kotlinSerializationOrderedAheadOfJsonConverter() {
|
||||
allOptionsRunner().run((context) -> {
|
||||
assertConverterBeanExists(context, KotlinSerializationJsonHttpMessageConverter.class,
|
||||
"kotlinSerializationJsonHttpMessageConverter");
|
||||
assertConverterBeanRegisteredWithHttpMessageConverters(context,
|
||||
KotlinSerializationJsonHttpMessageConverter.class);
|
||||
assertConvertersBeanRegisteredWithHttpMessageConverters(context,
|
||||
List.of(KotlinSerializationJsonHttpMessageConverter.class, JacksonJsonHttpMessageConverter.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -240,7 +237,6 @@ class HttpMessageConvertersAutoConfigurationTests {
|
||||
assertConverterBeanRegisteredWithHttpMessageConverters(context, JacksonJsonHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(GsonHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(JsonbHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(KotlinSerializationJsonHttpMessageConverter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -251,7 +247,6 @@ class HttpMessageConvertersAutoConfigurationTests {
|
||||
assertConverterBeanExists(context, GsonHttpMessageConverter.class, "gsonHttpMessageConverter");
|
||||
assertConverterBeanRegisteredWithHttpMessageConverters(context, GsonHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(JsonbHttpMessageConverter.class);
|
||||
assertThat(context).doesNotHaveBean(KotlinSerializationJsonHttpMessageConverter.class);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -327,6 +322,15 @@ class HttpMessageConvertersAutoConfigurationTests {
|
||||
assertThat(converters.getConverters()).contains(converter);
|
||||
}
|
||||
|
||||
private void assertConvertersBeanRegisteredWithHttpMessageConverters(AssertableApplicationContext context,
|
||||
List<Class<? extends HttpMessageConverter<?>>> types) {
|
||||
|
||||
List<? extends HttpMessageConverter<?>> converterInstances = types.stream().map(context::getBean).toList();
|
||||
|
||||
HttpMessageConverters converters = context.getBean(HttpMessageConverters.class);
|
||||
assertThat(converters.getConverters()).containsSubsequence(converterInstances);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class JacksonJsonMapperConfig {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user