Polish "Add Jsonb support"

Closes gh-9648
This commit is contained in:
Stephane Nicoll 2017-09-21 14:58:56 +02:00
parent 97aeaa4a32
commit 8d63b7458c
8 changed files with 205 additions and 249 deletions

View File

@ -43,7 +43,7 @@ class GsonHttpMessageConvertersConfiguration {
@Configuration @Configuration
@ConditionalOnBean(Gson.class) @ConditionalOnBean(Gson.class)
@Conditional(PreferGsonOrMissingJacksonAndJsonbCondition.class) @Conditional(PreferGsonOrJacksonAndJsonbUnavailableCondition.class)
protected static class GsonHttpMessageConverterConfiguration { protected static class GsonHttpMessageConverterConfiguration {
@Bean @Bean
@ -56,27 +56,28 @@ class GsonHttpMessageConvertersConfiguration {
} }
private static class PreferGsonOrMissingJacksonAndJsonbCondition extends AnyNestedCondition { private static class PreferGsonOrJacksonAndJsonbUnavailableCondition
extends AnyNestedCondition {
PreferGsonOrMissingJacksonAndJsonbCondition() { PreferGsonOrJacksonAndJsonbUnavailableCondition() {
super(ConfigurationPhase.REGISTER_BEAN); super(ConfigurationPhase.REGISTER_BEAN);
} }
@ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "gson", matchIfMissing = false) @ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "gson")
static class GsonPreferred { static class GsonPreferred {
} }
@Conditional(JacksonAndJsonbMissing.class) @Conditional(JacksonAndJsonbUnavailable.class)
static class JacksonJsonbMissing { static class JacksonJsonbUnavailable {
} }
} }
private static class JacksonAndJsonbMissing extends NoneNestedConditions { private static class JacksonAndJsonbUnavailable extends NoneNestedConditions {
JacksonAndJsonbMissing() { JacksonAndJsonbUnavailable() {
super(ConfigurationPhase.REGISTER_BEAN); super(ConfigurationPhase.REGISTER_BEAN);
} }
@ -86,7 +87,7 @@ class GsonHttpMessageConvertersConfiguration {
} }
@ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "jsonb") @ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "jsonb")
static class JsonbMissing { static class JsonbPreferred {
} }

View File

@ -23,10 +23,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 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.Bean;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.GsonHttpMessageConverter;
import org.springframework.http.converter.json.JsonbHttpMessageConverter; import org.springframework.http.converter.json.JsonbHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
@ -55,40 +55,24 @@ class JsonbHttpMessageConvertersConfiguration {
} }
private static class PreferJsonbOrMissingJacksonAndGsonCondition extends AnyNestedCondition { private static class PreferJsonbOrMissingJacksonAndGsonCondition
extends AnyNestedCondition {
PreferJsonbOrMissingJacksonAndGsonCondition() { PreferJsonbOrMissingJacksonAndGsonCondition() {
super(ConfigurationPhase.REGISTER_BEAN); super(ConfigurationPhase.REGISTER_BEAN);
} }
@ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "jsonb", matchIfMissing = false) @ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "jsonb")
static class JsonbPreferred { static class JsonbPreferred {
} }
@Conditional(JacksonAndGsonMissing.class) @ConditionalOnMissingBean({ MappingJackson2HttpMessageConverter.class, GsonHttpMessageConverter.class })
static class JacksonGsonMissing { static class JacksonAndGsonMissing {
} }
} }
private static class JacksonAndGsonMissing extends NoneNestedConditions {
JacksonAndGsonMissing() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnBean(MappingJackson2HttpMessageConverter.class)
static class JacksonMissing {
}
@ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "gson")
static class GsonMissing {
}
}
} }

View File

@ -200,7 +200,7 @@
{ {
"name": "spring.http.converters.preferred-json-mapper", "name": "spring.http.converters.preferred-json-mapper",
"type": "java.lang.String", "type": "java.lang.String",
"description": "Preferred JSON mapper to use for HTTP message conversion. Set to \"gson\" to force the use of Gson when both it and Jackson are on the classpath." "description": "Preferred JSON mapper to use for HTTP message conversion, auto-detected according to the environment by default."
}, },
{ {
"name": "spring.jersey.type", "name": "spring.jersey.type",

View File

@ -16,28 +16,29 @@
package org.springframework.boot.autoconfigure.http; package org.springframework.boot.autoconfigure.http;
import java.util.Arrays;
import java.util.List;
import javax.json.bind.Jsonb; import javax.json.bind.Jsonb;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson; import com.google.gson.Gson;
import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration; import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration;
import org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration.MappingJackson2HttpMessageConverterConfiguration; import org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration.MappingJackson2HttpMessageConverterConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration; import org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration;
import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.test.context.HidePackagesClassLoader;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.runner.ContextConsumer;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration; import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
import org.springframework.hateoas.ResourceSupport; import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter; import org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.GsonHttpMessageConverter; import org.springframework.http.converter.json.GsonHttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
@ -59,240 +60,216 @@ import static org.assertj.core.api.Assertions.assertThat;
*/ */
public class HttpMessageConvertersAutoConfigurationTests { public class HttpMessageConvertersAutoConfigurationTests {
private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(
HttpMessageConvertersAutoConfiguration.class));
@After @Test
public void close() { public void jacksonNotAvailable() {
if (this.context != null) { this.contextRunner.run((context) -> {
this.context.close(); assertThat(context).doesNotHaveBean(ObjectMapper.class);
} assertThat(context).doesNotHaveBean(MappingJackson2HttpMessageConverter.class);
assertThat(context).doesNotHaveBean(MappingJackson2XmlHttpMessageConverter.class);
});
} }
@Test @Test
public void noObjectMapperMeansNoConverter() throws Exception { public void jacksonDefaultConverter() {
this.context.register(HttpMessageConvertersAutoConfiguration.class); this.contextRunner.withUserConfiguration(JacksonObjectMapperConfig.class)
this.context.refresh(); .run(assertConverter(MappingJackson2HttpMessageConverter.class,
assertThat(this.context.getBeansOfType(ObjectMapper.class)).isEmpty(); "mappingJackson2HttpMessageConverter"));
assertThat(this.context.getBeansOfType(MappingJackson2HttpMessageConverter.class))
.isEmpty();
assertThat(
this.context.getBeansOfType(MappingJackson2XmlHttpMessageConverter.class))
.isEmpty();
} }
@Test @Test
public void defaultJacksonConverter() throws Exception { public void jacksonConverterWithBuilder() {
this.context.register(JacksonObjectMapperConfig.class, this.contextRunner.withUserConfiguration(JacksonObjectMapperBuilderConfig.class)
HttpMessageConvertersAutoConfiguration.class); .run(assertConverter(MappingJackson2HttpMessageConverter.class,
this.context.refresh(); "mappingJackson2HttpMessageConverter"));
assertConverterBeanExists(MappingJackson2HttpMessageConverter.class,
"mappingJackson2HttpMessageConverter");
assertConverterBeanRegisteredWithHttpMessageConverters(
MappingJackson2HttpMessageConverter.class);
} }
@Test @Test
public void defaultJacksonConvertersWithBuilder() throws Exception { public void jacksonXmlConverterWithBuilder() {
this.context.register(JacksonObjectMapperBuilderConfig.class, this.contextRunner.withUserConfiguration(JacksonObjectMapperBuilderConfig.class)
HttpMessageConvertersAutoConfiguration.class); .run(assertConverter(MappingJackson2XmlHttpMessageConverter.class,
this.context.refresh(); "mappingJackson2XmlHttpMessageConverter"));
assertConverterBeanExists(MappingJackson2HttpMessageConverter.class,
"mappingJackson2HttpMessageConverter");
assertConverterBeanExists(MappingJackson2XmlHttpMessageConverter.class,
"mappingJackson2XmlHttpMessageConverter");
assertConverterBeanRegisteredWithHttpMessageConverters(
MappingJackson2HttpMessageConverter.class);
assertConverterBeanRegisteredWithHttpMessageConverters(
MappingJackson2XmlHttpMessageConverter.class);
} }
@Test @Test
public void customJacksonConverter() throws Exception { public void jacksonCustomConverter() {
this.context.register(JacksonObjectMapperConfig.class, this.contextRunner.withUserConfiguration(JacksonObjectMapperConfig.class,
JacksonConverterConfig.class, JacksonConverterConfig.class
HttpMessageConvertersAutoConfiguration.class); ).run(assertConverter(MappingJackson2HttpMessageConverter.class,
this.context.refresh(); "customJacksonMessageConverter"));
assertConverterBeanExists(MappingJackson2HttpMessageConverter.class,
"customJacksonMessageConverter");
} }
@Test @Test
public void noGson() throws Exception { public void gsonNotAvailable() {
this.context.register(HttpMessageConvertersAutoConfiguration.class); this.contextRunner.run((context) -> {
this.context.refresh(); assertThat(context).doesNotHaveBean(Gson.class);
assertThat(this.context.getBeansOfType(Gson.class).isEmpty()).isTrue(); assertThat(context).doesNotHaveBean(GsonHttpMessageConverter.class);
assertThat(this.context.getBeansOfType(GsonHttpMessageConverter.class).isEmpty()) });
.isTrue();
} }
@Test @Test
public void defaultGsonConverter() throws Exception { public void gsonDefaultConverter() {
this.context.register(GsonAutoConfiguration.class, this.contextRunner.withConfiguration(AutoConfigurations.of(
HttpMessageConvertersAutoConfiguration.class); GsonAutoConfiguration.class)
this.context.refresh(); ).run(assertConverter(GsonHttpMessageConverter.class,
assertConverterBeanExists(GsonHttpMessageConverter.class, "gsonHttpMessageConverter"));
"gsonHttpMessageConverter");
assertConverterBeanRegisteredWithHttpMessageConverters(
GsonHttpMessageConverter.class);
} }
@Test @Test
public void jacksonIsPreferredByDefaultWhenBothGsonAndJacksonAreAvailable() { public void gsonCustomConverter() {
this.context.register(GsonAutoConfiguration.class, JacksonAutoConfiguration.class, this.contextRunner.withUserConfiguration(GsonConverterConfig.class)
JsonbAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class); .withConfiguration(AutoConfigurations.of(GsonAutoConfiguration.class))
this.context.refresh(); .run(assertConverter(GsonHttpMessageConverter.class,
assertConverterBeanExists(MappingJackson2HttpMessageConverter.class, "customGsonMessageConverter"));
"mappingJackson2HttpMessageConverter");
assertConverterBeanRegisteredWithHttpMessageConverters(
MappingJackson2HttpMessageConverter.class);
assertThat(this.context.getBeansOfType(GsonHttpMessageConverter.class)).isEmpty();
assertThat(this.context.getBeansOfType(JsonbHttpMessageConverter.class)).isEmpty();
} }
@Test @Test
public void gsonCanBePreferredWhenBothGsonAndJacksonAreAvailable() { public void gsonCanBePreferred() {
this.context.register(GsonAutoConfiguration.class, JacksonAutoConfiguration.class, allOptionsRunner().withPropertyValues(
JsonbAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class); "spring.http.converters.preferred-json-mapper:gson").run((context) -> {
TestPropertyValues.of("spring.http.converters.preferred-json-mapper:gson") assertConverterBeanExists(context, GsonHttpMessageConverter.class,
.applyTo(this.context); "gsonHttpMessageConverter");
this.context.refresh(); assertConverterBeanRegisteredWithHttpMessageConverters(context,
assertConverterBeanExists(GsonHttpMessageConverter.class, GsonHttpMessageConverter.class);
"gsonHttpMessageConverter"); assertThat(context).doesNotHaveBean(JsonbHttpMessageConverter.class);
assertConverterBeanRegisteredWithHttpMessageConverters( assertThat(context).doesNotHaveBean(MappingJackson2HttpMessageConverter.class);
GsonHttpMessageConverter.class); });
assertThat(this.context.getBeansOfType(JsonbHttpMessageConverter.class))
.isEmpty();
assertThat(this.context.getBeansOfType(MappingJackson2HttpMessageConverter.class))
.isEmpty();
} }
@Test @Test
public void customGsonConverter() throws Exception { public void jsonbNotAvailable() {
this.context.register(GsonAutoConfiguration.class, GsonConverterConfig.class, this.contextRunner.run((context) -> {
HttpMessageConvertersAutoConfiguration.class); assertThat(context).doesNotHaveBean(Jsonb.class);
this.context.refresh(); assertThat(context).doesNotHaveBean(JsonbHttpMessageConverter.class);
assertConverterBeanExists(GsonHttpMessageConverter.class, });
"customGsonMessageConverter");
assertConverterBeanRegisteredWithHttpMessageConverters(
GsonHttpMessageConverter.class);
} }
@Test @Test
public void noJsonb() throws Exception { public void jsonbDefaultConverter() {
this.context.register(HttpMessageConvertersAutoConfiguration.class); this.contextRunner.withConfiguration(AutoConfigurations.of(
this.context.refresh(); JsonbAutoConfiguration.class)
assertThat(this.context.getBeansOfType(Jsonb.class).isEmpty()).isTrue(); ).run(assertConverter(JsonbHttpMessageConverter.class,
assertThat(this.context.getBeansOfType(JsonbHttpMessageConverter.class).isEmpty()) "jsonbHttpMessageConverter"));
.isTrue();
} }
@Test @Test
public void defaultJsonbConverter() throws Exception { public void jsonbCustomConverter() {
this.context.register(JsonbAutoConfiguration.class, this.contextRunner.withUserConfiguration(JsonbConverterConfig.class)
HttpMessageConvertersAutoConfiguration.class); .withConfiguration(AutoConfigurations.of(JsonbAutoConfiguration.class))
this.context.refresh(); .run(assertConverter(JsonbHttpMessageConverter.class,
assertConverterBeanExists(JsonbHttpMessageConverter.class, "customJsonbMessageConverter"));
"jsonbHttpMessageConverter");
assertConverterBeanRegisteredWithHttpMessageConverters(
JsonbHttpMessageConverter.class);
} }
@Test @Test
public void jsonbCanBePreferredWhenBothGsonAndJacksonAreAvailable() { public void jsonbCanBePreferred() {
this.context.register(GsonAutoConfiguration.class, JacksonAutoConfiguration.class, allOptionsRunner().withPropertyValues(
JsonbAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class); "spring.http.converters.preferred-json-mapper:jsonb").run((context) -> {
TestPropertyValues.of("spring.http.converters.preferred-json-mapper:jsonb") assertConverterBeanExists(context, JsonbHttpMessageConverter.class,
.applyTo(this.context); "jsonbHttpMessageConverter");
this.context.refresh(); assertConverterBeanRegisteredWithHttpMessageConverters(context,
assertConverterBeanExists(JsonbHttpMessageConverter.class, JsonbHttpMessageConverter.class);
"jsonbHttpMessageConverter"); assertThat(context).doesNotHaveBean(GsonHttpMessageConverter.class);
assertConverterBeanRegisteredWithHttpMessageConverters( assertThat(context).doesNotHaveBean(MappingJackson2HttpMessageConverter.class);
JsonbHttpMessageConverter.class); });
assertThat(this.context.getBeansOfType(GsonHttpMessageConverter.class))
.isEmpty();
assertThat(this.context.getBeansOfType(MappingJackson2HttpMessageConverter.class))
.isEmpty();
} }
@Test @Test
public void customJsonbConverter() throws Exception { public void stringDefaultConverter() {
this.context.register(JsonbAutoConfiguration.class, JsonbConverterConfig.class, this.contextRunner.run(assertConverter(StringHttpMessageConverter.class,
HttpMessageConvertersAutoConfiguration.class); "stringHttpMessageConverter"));
this.context.refresh();
assertConverterBeanExists(JsonbHttpMessageConverter.class,
"customJsonbMessageConverter");
assertConverterBeanRegisteredWithHttpMessageConverters(
JsonbHttpMessageConverter.class);
} }
@Test @Test
public void defaultStringConverter() throws Exception { public void sringCustomConverter() {
this.context.register(HttpMessageConvertersAutoConfiguration.class); this.contextRunner.withUserConfiguration(StringConverterConfig.class)
this.context.refresh(); .run(assertConverter(StringHttpMessageConverter.class,
assertConverterBeanExists(StringHttpMessageConverter.class, "customStringMessageConverter"));
"stringHttpMessageConverter");
assertConverterBeanRegisteredWithHttpMessageConverters(
StringHttpMessageConverter.class);
} }
@Test @Test
public void customStringConverter() throws Exception { public void typeConstrainedConverterDoesNotPreventAutoConfigurationOfJacksonConverter() {
this.context.register(StringConverterConfig.class, this.contextRunner.withUserConfiguration(JacksonObjectMapperBuilderConfig.class,
HttpMessageConvertersAutoConfiguration.class); TypeConstrainedConverterConfiguration.class).run((context) -> {
this.context.refresh(); BeanDefinition beanDefinition = ((GenericApplicationContext) context.getSourceApplicationContext())
assertConverterBeanExists(StringHttpMessageConverter.class, .getBeanDefinition("mappingJackson2HttpMessageConverter");
"customStringMessageConverter"); assertThat(beanDefinition.getFactoryBeanName()).isEqualTo(
MappingJackson2HttpMessageConverterConfiguration.class.getName());
assertConverterBeanRegisteredWithHttpMessageConverters( });
StringHttpMessageConverter.class);
} }
@Test @Test
public void typeConstrainedConverterDoesNotPreventAutoConfigurationOfJacksonConverter() public void typeConstrainedConverterFromSpringDataDoesNotPreventAutoConfigurationOfJacksonConverter() {
throws Exception { this.contextRunner.withUserConfiguration(JacksonObjectMapperBuilderConfig.class,
this.context.register(JacksonObjectMapperBuilderConfig.class, RepositoryRestMvcConfiguration.class).run((context) -> {
TypeConstrainedConverterConfiguration.class, BeanDefinition beanDefinition = ((GenericApplicationContext) context.getSourceApplicationContext())
HttpMessageConvertersAutoConfiguration.class); .getBeanDefinition("mappingJackson2HttpMessageConverter");
this.context.refresh(); assertThat(beanDefinition.getFactoryBeanName()).isEqualTo(
MappingJackson2HttpMessageConverterConfiguration.class.getName());
BeanDefinition beanDefinition = this.context });
.getBeanDefinition("mappingJackson2HttpMessageConverter");
assertThat(beanDefinition.getFactoryBeanName()).isEqualTo(
MappingJackson2HttpMessageConverterConfiguration.class.getName());
} }
@Test @Test
public void typeConstrainedConverterFromSpringDataDoesNotPreventAutoConfigurationOfJacksonConverter() public void jacksonIsPreferredByDefault() {
throws Exception { allOptionsRunner().run((context) -> {
this.context.register(JacksonObjectMapperBuilderConfig.class, assertConverterBeanExists(context, MappingJackson2HttpMessageConverter.class,
RepositoryRestMvcConfiguration.class, "mappingJackson2HttpMessageConverter");
HttpMessageConvertersAutoConfiguration.class); assertConverterBeanRegisteredWithHttpMessageConverters(context,
this.context.refresh(); MappingJackson2HttpMessageConverter.class);
BeanDefinition beanDefinition = this.context assertThat(context).doesNotHaveBean(GsonHttpMessageConverter.class);
.getBeanDefinition("mappingJackson2HttpMessageConverter"); assertThat(context).doesNotHaveBean(JsonbHttpMessageConverter.class);
assertThat(beanDefinition.getFactoryBeanName()).isEqualTo( });
MappingJackson2HttpMessageConverterConfiguration.class.getName());
} }
private void assertConverterBeanExists(Class<?> type, String beanName) { @Test
assertThat(this.context.getBeansOfType(type)).hasSize(1); public void gsonIsPreferredIfJacksonIsNotAvailable() {
List<String> beanNames = Arrays.asList(this.context.getBeanDefinitionNames()); allOptionsRunner()
assertThat(beanNames).contains(beanName); .withClassLoader(new HidePackagesClassLoader(
ObjectMapper.class.getPackage().getName())).run((context) -> {
assertConverterBeanExists(context, GsonHttpMessageConverter.class,
"gsonHttpMessageConverter");
assertThat(context).doesNotHaveBean(JsonbHttpMessageConverter.class);
});
} }
private void assertConverterBeanRegisteredWithHttpMessageConverters(Class<?> type) { @Test
Object converter = this.context.getBean(type); public void jsonbIsPreferredIfJacksonAndGsonAreNotAvailable() {
HttpMessageConverters converters = this.context allOptionsRunner()
.getBean(HttpMessageConverters.class); .withClassLoader(new HidePackagesClassLoader(
assertThat(converters.getConverters().contains(converter)).isTrue(); ObjectMapper.class.getPackage().getName(),
Gson.class.getPackage().getName()))
.run(assertConverter(JsonbHttpMessageConverter.class,
"jsonbHttpMessageConverter"));
}
private ApplicationContextRunner allOptionsRunner() {
return this.contextRunner.withConfiguration(AutoConfigurations.of(
GsonAutoConfiguration.class, JacksonAutoConfiguration.class,
JsonbAutoConfiguration.class));
}
private ContextConsumer<AssertableApplicationContext> assertConverter(
Class<? extends HttpMessageConverter> converterType, String beanName) {
return context -> {
assertConverterBeanExists(context, converterType, beanName);
assertConverterBeanRegisteredWithHttpMessageConverters(context, converterType);
};
}
private void assertConverterBeanExists(AssertableApplicationContext context,
Class<?> type, String beanName) {
assertThat(context).hasSingleBean(type);
assertThat(context).hasBean(beanName);
}
private void assertConverterBeanRegisteredWithHttpMessageConverters(
AssertableApplicationContext context, Class<? extends HttpMessageConverter> type) {
HttpMessageConverter converter = context.getBean(type);
HttpMessageConverters converters = context.getBean(HttpMessageConverters.class);
assertThat(converters.getConverters()).contains(converter);
} }
@Configuration @Configuration

View File

@ -18,11 +18,10 @@ package org.springframework.boot.autoconfigure.jsonb;
import javax.json.bind.Jsonb; import javax.json.bind.Jsonb;
import org.junit.After;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -33,26 +32,16 @@ import static org.assertj.core.api.Assertions.assertThat;
*/ */
public class JsonbAutoConfigurationTests { public class JsonbAutoConfigurationTests {
AnnotationConfigApplicationContext context; private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(JsonbAutoConfiguration.class));
@Before
public void setUp() {
this.context = new AnnotationConfigApplicationContext();
}
@After
public void tearDown() {
if (this.context != null) {
this.context.close();
}
}
@Test @Test
public void jsonbRegistration() { public void jsonbRegistration() {
this.context.register(JsonbAutoConfiguration.class); this.contextRunner.run((context) -> {
this.context.refresh(); assertThat(context).hasSingleBean(Jsonb.class);
Jsonb jsonb = this.context.getBean(Jsonb.class); Jsonb jsonb = context.getBean(Jsonb.class);
assertThat(jsonb.toJson(new DataObject())).isEqualTo("{\"data\":\"hello\"}"); assertThat(jsonb.toJson(new DataObject())).isEqualTo("{\"data\":\"hello\"}");
});
} }
public class DataObject { public class DataObject {

View File

@ -297,7 +297,7 @@ content into your application; rather pick only the properties that you need.
spring.hateoas.use-hal-as-default-json-media-type=true # Specify if application/hal+json responses should be sent to requests that accept application/json. spring.hateoas.use-hal-as-default-json-media-type=true # Specify if application/hal+json responses should be sent to requests that accept application/json.
# HTTP message conversion # HTTP message conversion
spring.http.converters.preferred-json-mapper=jackson # Preferred JSON mapper to use for HTTP message conversion. Set to "gson" to force the use of Gson when both it and Jackson are on the classpath. spring.http.converters.preferred-json-mapper= # Preferred JSON mapper to use for HTTP message conversion, auto-detected according to the environment by default.
# HTTP encoding ({sc-spring-boot-autoconfigure}/http/HttpEncodingProperties.{sc-ext}[HttpEncodingProperties]) # HTTP encoding ({sc-spring-boot-autoconfigure}/http/HttpEncodingProperties.{sc-ext}[HttpEncodingProperties])
spring.http.encoding.charset=UTF-8 # Charset of HTTP requests and responses. Added to the "Content-Type" header if not set explicitly. spring.http.encoding.charset=UTF-8 # Charset of HTTP requests and responses. Added to the "Content-Type" header if not set explicitly.

View File

@ -5675,16 +5675,21 @@ TIP: It's also possible to use the `@AutoConfigure...` annotations with the stan
[[boot-features-testing-spring-boot-applications-testing-autoconfigured-json-tests]] [[boot-features-testing-spring-boot-applications-testing-autoconfigured-json-tests]]
==== Auto-configured JSON tests ==== Auto-configured JSON tests
To test that Object JSON serialization and deserialization is working as expected you can To test that Object JSON serialization and deserialization is working as expected you can
use the `@JsonTest` annotation. `@JsonTest` will auto-configure Jackson `ObjectMapper`, use the `@JsonTest` annotation. `@JsonTest` will auto-configure the available supported
any `@JsonComponent` beans and any Jackson `Modules`. It also configures `Gson` json mapper:
if you happen to be using that instead of, or as well as, Jackson. If you need to
configure elements of the auto-configuration you can use the `@AutoConfigureJsonTesters` * Jackson `ObjectMapper`, any `@JsonComponent` beans and any Jackson `Modules`
annotation. * `Gson`
* `Jsonb`
If you need to configure elements of the auto-configuration you can use the
`@AutoConfigureJsonTesters` annotation.
Spring Boot includes AssertJ based helpers that work with the JSONassert and JsonPath Spring Boot includes AssertJ based helpers that work with the JSONassert and JsonPath
libraries to check that JSON is as expected. The `JacksonTester`, `GsonTester` and libraries to check that JSON is as expected. The `JacksonTester`, `GsonTester`,
`BasicJsonTester` classes can be used for Jackson, Gson and Strings respectively. Any `JsonbTester` and `BasicJsonTester` classes can be used for Jackson, Gson, Jsonb and
helper fields on the test class can be `@Autowired` when using `@JsonTest`. Strings respectively. Any helper fields on the test class can be `@Autowired` when using
`@JsonTest`.
[source,java,indent=0] [source,java,indent=0]
---- ----
@ -6237,9 +6242,9 @@ A list of the auto-configuration that is enabled by `@DataLdapTest` can be
[[boot-features-testing-spring-boot-applications-testing-autoconfigured-rest-client]] [[boot-features-testing-spring-boot-applications-testing-autoconfigured-rest-client]]
==== Auto-configured REST clients ==== Auto-configured REST clients
The `@RestClientTest` annotation can be used if you want to test REST clients. By default The `@RestClientTest` annotation can be used if you want to test REST clients. By default
it will auto-configure Jackson and GSON support, configure a `RestTemplateBuilder` and it will auto-configure Jackson, GSON and Jsonb support, configure a `RestTemplateBuilder`
add support for `MockRestServiceServer`. The specific beans that you want to test should and add support for `MockRestServiceServer`. The specific beans that you want to test
be specified using `value` or `components` attribute of `@RestClientTest`: should be specified using `value` or `components` attribute of `@RestClientTest`:
[source,java,indent=0] [source,java,indent=0]

View File

@ -126,7 +126,7 @@ org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\ org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\ org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\ org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration.\ org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\ org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\ org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\