From de8a2a71d0146dd7be60475e5602deec401179b8 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 2 Oct 2014 10:27:21 +0100 Subject: [PATCH] Turn (Jackson|Gson)AutoConfiguration into true auto-configuration Previously JacksonAutoConfiguration and GsonAutoConfiguration were not actually auto-configuration classes. They were only processed due to being imported by HttpMessageConvertersAutoConfiguration. In addition to being misleadingly named, this meant that they could not be included or excluded individually and were also tightly coupled to HTTP message conversion. This commit updates spring.factories to make both JacksonAutoConfiguration and GsonAutoConfiguration actual auto-configuration classes. As a result, they can now be enabled or disabled individually and are no longer coupled to HTTP message conversion. Closes gh-1562 --- ...ttpMessageConvertersAutoConfiguration.java | 12 +- .../main/resources/META-INF/spring.factories | 2 + ...ssageConvertersAutoConfigurationTests.java | 123 +++++++++++++----- 3 files changed, 99 insertions(+), 38 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/HttpMessageConvertersAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/HttpMessageConvertersAutoConfiguration.java index 3f149066a6..68b5b910d7 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/HttpMessageConvertersAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/HttpMessageConvertersAutoConfiguration.java @@ -21,14 +21,12 @@ import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +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.gson.GsonAutoConfiguration; -import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.GsonHttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; @@ -44,10 +42,10 @@ import com.google.gson.Gson; * @author Piotr Maj * @author Oliver Gierke * @author David Liu + * @author Andy Wilkinson */ @Configuration @ConditionalOnClass(HttpMessageConverter.class) -@Import({ JacksonAutoConfiguration.class, GsonAutoConfiguration.class }) public class HttpMessageConvertersAutoConfiguration { @Autowired(required = false) @@ -61,8 +59,9 @@ public class HttpMessageConvertersAutoConfiguration { @Configuration @ConditionalOnClass(ObjectMapper.class) + @ConditionalOnBean(ObjectMapper.class) @EnableConfigurationProperties(HttpMapperProperties.class) - protected static class ObjectMappers { + protected static class MappingJackson2HttpMessageConverterConfiguration { @Autowired private HttpMapperProperties properties = new HttpMapperProperties(); @@ -81,7 +80,8 @@ public class HttpMessageConvertersAutoConfiguration { @Configuration @ConditionalOnClass(Gson.class) - protected static class GsonConfiguration { + @ConditionalOnBean(Gson.class) + protected static class GsonHttpMessageConverterConfiguration { @Bean @ConditionalOnMissingBean diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index 8fe6706801..a997d9f8b7 100644 --- a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -16,8 +16,10 @@ org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfigura org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\ +org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\ org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\ org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\ +org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\ diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/HttpMessageConvertersAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/HttpMessageConvertersAutoConfigurationTests.java index 60eb767f28..1c4368101c 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/HttpMessageConvertersAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/HttpMessageConvertersAutoConfigurationTests.java @@ -16,6 +16,9 @@ package org.springframework.boot.autoconfigure.web; +import java.util.Arrays; +import java.util.List; + import org.junit.After; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -36,6 +39,7 @@ import static org.junit.Assert.assertTrue; * @author Dave Syer * @author Oliver Gierke * @author David Liu + * @author Andy Wilkinson */ public class HttpMessageConvertersAutoConfigurationTests { @@ -49,14 +53,75 @@ public class HttpMessageConvertersAutoConfigurationTests { } @Test - public void customJacksonConverter() throws Exception { + public void noObjectMapperMeansNoConverter() throws Exception { + this.context.register(HttpMessageConvertersAutoConfiguration.class); + this.context.refresh(); + assertTrue(this.context.getBeansOfType(ObjectMapper.class).isEmpty()); + assertTrue(this.context.getBeansOfType(MappingJackson2HttpMessageConverter.class) + .isEmpty()); + } + + @Test + public void defaultJacksonConverter() throws Exception { this.context.register(JacksonConfig.class, HttpMessageConvertersAutoConfiguration.class); this.context.refresh(); - MappingJackson2HttpMessageConverter converter = this.context - .getBean(MappingJackson2HttpMessageConverter.class); - assertEquals(this.context.getBean(ObjectMapper.class), - converter.getObjectMapper()); + + assertConverterBeanExists(MappingJackson2HttpMessageConverter.class, + "mappingJackson2HttpMessageConverter"); + + assertConverterBeanRegisteredWithHttpMessageConverters(MappingJackson2HttpMessageConverter.class); + } + + @Test + public void customJacksonConverter() throws Exception { + this.context.register(JacksonConfig.class, JacksonConverterConfig.class, + HttpMessageConvertersAutoConfiguration.class); + this.context.refresh(); + + assertConverterBeanExists(MappingJackson2HttpMessageConverter.class, + "customJacksonMessageConverter"); + } + + @Test + public void noGson() throws Exception { + this.context.register(HttpMessageConvertersAutoConfiguration.class); + this.context.refresh(); + assertTrue(this.context.getBeansOfType(Gson.class).isEmpty()); + assertTrue(this.context.getBeansOfType(GsonHttpMessageConverter.class).isEmpty()); + } + + @Test + public void defaultGsonConverter() throws Exception { + this.context.register(GsonConfig.class, + HttpMessageConvertersAutoConfiguration.class); + this.context.refresh(); + assertConverterBeanExists(GsonHttpMessageConverter.class, + "gsonHttpMessageConverter"); + + assertConverterBeanRegisteredWithHttpMessageConverters(GsonHttpMessageConverter.class); + } + + @Test + public void customGsonConverter() throws Exception { + this.context.register(GsonConfig.class, GsonConverterConfig.class, + HttpMessageConvertersAutoConfiguration.class); + this.context.refresh(); + assertConverterBeanExists(GsonHttpMessageConverter.class, + "customGsonMessageConverter"); + + assertConverterBeanRegisteredWithHttpMessageConverters(GsonHttpMessageConverter.class); + } + + private void assertConverterBeanExists(Class type, String beanName) { + assertEquals(1, this.context.getBeansOfType(type).size()); + List beanNames = Arrays.asList(this.context.getBeanDefinitionNames()); + assertTrue(beanName + " not found in " + beanNames, beanNames.contains(beanName)); + } + + private void assertConverterBeanRegisteredWithHttpMessageConverters(Class type) { + + Object converter = this.context.getBean(type); HttpMessageConverters converters = this.context .getBean(HttpMessageConverters.class); assertTrue(converters.getConverters().contains(converter)); @@ -64,48 +129,42 @@ public class HttpMessageConvertersAutoConfigurationTests { @Configuration protected static class JacksonConfig { - - @Bean - public MappingJackson2HttpMessageConverter jacksonMessageConverter() { - MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); - converter.setObjectMapper(objectMapper()); - return converter; - } - @Bean public ObjectMapper objectMapper() { return new ObjectMapper(); } - } - @Test - public void customGsonConverter() throws Exception { - this.context.register(GsonConfig.class, - HttpMessageConvertersAutoConfiguration.class); - this.context.refresh(); - GsonHttpMessageConverter converter = this.context - .getBean(GsonHttpMessageConverter.class); - assertEquals(this.context.getBean(Gson.class), converter.getGson()); - HttpMessageConverters converters = this.context - .getBean(HttpMessageConverters.class); - assertTrue(converters.getConverters().contains(converter)); + @Configuration + protected static class JacksonConverterConfig { + + @Bean + public MappingJackson2HttpMessageConverter customJacksonMessageConverter( + ObjectMapper objectMapper) { + MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); + converter.setObjectMapper(objectMapper); + return converter; + } } @Configuration protected static class GsonConfig { - @Bean - public GsonHttpMessageConverter gsonMessageConverter() { - GsonHttpMessageConverter converter = new GsonHttpMessageConverter(); - converter.setGson(gson()); - return converter; - } - @Bean public Gson gson() { return new Gson(); } } + @Configuration + protected static class GsonConverterConfig { + + @Bean + public GsonHttpMessageConverter customGsonMessageConverter(Gson gson) { + GsonHttpMessageConverter converter = new GsonHttpMessageConverter(); + converter.setGson(gson); + return converter; + } + } + }