diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/GsonHttpMessageConvertersConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/GsonHttpMessageConvertersConfiguration.java new file mode 100644 index 0000000000..eaf0eb237c --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/GsonHttpMessageConvertersConfiguration.java @@ -0,0 +1,75 @@ +/* + * Copyright 2012-2015 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 + * + * http://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.web; + +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.MappingJackson2HttpMessageConverter; + +import com.google.gson.Gson; + +/** + * Configuration for HTTP Message converters that use Gson + * + * @author awilkinson + * @since 1.2.2 + */ +@Configuration +class GsonHttpMessageConvertersConfiguration { + + @Configuration + @ConditionalOnClass(Gson.class) + @ConditionalOnBean(Gson.class) + @Conditional(PreferGsonOrMissingJacksonCondition.class) + protected static class GsonHttpMessageConverterConfiguration { + + @Bean + @ConditionalOnMissingBean + public GsonHttpMessageConverter gsonHttpMessageConverter(Gson gson) { + GsonHttpMessageConverter converter = new GsonHttpMessageConverter(); + converter.setGson(gson); + return converter; + } + + } + + private static class PreferGsonOrMissingJacksonCondition extends AnyNestedCondition { + + public PreferGsonOrMissingJacksonCondition() { + super(ConfigurationPhase.REGISTER_BEAN); + } + + @ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "gson", matchIfMissing = false) + static class GsonPreferred { + + } + + @ConditionalOnMissingBean(MappingJackson2HttpMessageConverter.class) + static class JacksonMissing { + + } + + } + +} 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 28ac65b490..2673f5ab77 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 @@ -22,7 +22,6 @@ import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; 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; @@ -30,16 +29,9 @@ 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.StringHttpMessageConverter; -import org.springframework.http.converter.json.GsonHttpMessageConverter; -import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.xml.XmlMapper; -import com.google.gson.Gson; /** * {@link EnableAutoConfiguration Auto-configuration} for {@link HttpMessageConverter}s. @@ -56,8 +48,12 @@ import com.google.gson.Gson; @Configuration @ConditionalOnClass(HttpMessageConverter.class) @AutoConfigureAfter({ GsonAutoConfiguration.class, JacksonAutoConfiguration.class }) +@Import({ JacksonHttpMessageConvertersConfiguration.class, + GsonHttpMessageConvertersConfiguration.class }) public class HttpMessageConvertersAutoConfiguration { + static final String PREFERRED_MAPPER_PROPERTY = "spring.http.converters.preferred-mapper"; + @Autowired(required = false) private final List> converters = Collections.emptyList(); @@ -67,72 +63,6 @@ public class HttpMessageConvertersAutoConfiguration { return new HttpMessageConverters(this.converters); } - @Configuration - @ConditionalOnClass(ObjectMapper.class) - @ConditionalOnBean(ObjectMapper.class) - @ConditionalOnMissingBean(GsonHttpMessageConverter.class) - @EnableConfigurationProperties(HttpMapperProperties.class) - @SuppressWarnings("deprecation") - protected static class MappingJackson2HttpMessageConverterConfiguration { - - // This can be removed when the deprecated class is removed (the ObjectMapper will - // already have all the correct properties). - @Autowired - private HttpMapperProperties properties = new HttpMapperProperties(); - - @Bean - @ConditionalOnMissingBean - public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter( - ObjectMapper objectMapper) { - MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter( - objectMapper); - if (this.properties.isJsonPrettyPrint() != null) { - converter.setPrettyPrint(this.properties.isJsonPrettyPrint()); - } - return converter; - } - - } - - @Configuration - @ConditionalOnClass(XmlMapper.class) - @ConditionalOnBean(Jackson2ObjectMapperBuilder.class) - @EnableConfigurationProperties(HttpMapperProperties.class) - @SuppressWarnings("deprecation") - protected static class MappingJackson2XmlHttpMessageConverterConfiguration { - - @Autowired - private HttpMapperProperties properties = new HttpMapperProperties(); - - @Bean - @ConditionalOnMissingBean - public MappingJackson2XmlHttpMessageConverter mappingJackson2XmlHttpMessageConverter( - Jackson2ObjectMapperBuilder builder) { - MappingJackson2XmlHttpMessageConverter converter = new MappingJackson2XmlHttpMessageConverter(); - converter.setObjectMapper(builder.createXmlMapper(true).build()); - if (this.properties.isJsonPrettyPrint() != null) { - converter.setPrettyPrint(this.properties.isJsonPrettyPrint()); - } - return converter; - } - - } - - @Configuration - @ConditionalOnClass(Gson.class) - @ConditionalOnBean(Gson.class) - protected static class GsonHttpMessageConverterConfiguration { - - @Bean - @ConditionalOnMissingBean - public GsonHttpMessageConverter gsonHttpMessageConverter(Gson gson) { - GsonHttpMessageConverter converter = new GsonHttpMessageConverter(); - converter.setGson(gson); - return converter; - } - - } - @Configuration @ConditionalOnClass(StringHttpMessageConverter.class) @EnableConfigurationProperties(HttpEncodingProperties.class) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/JacksonHttpMessageConvertersConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/JacksonHttpMessageConvertersConfiguration.java new file mode 100644 index 0000000000..9e315c6515 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/JacksonHttpMessageConvertersConfiguration.java @@ -0,0 +1,94 @@ +/* + * Copyright 2012-2015 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 + * + * http://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.web; + +import org.springframework.beans.factory.annotation.Autowired; +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.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; + +/** + * Configuration for HTTP message converters that use Jackson + * + * @author Andy Wilkinson + * @since 1.2.2 + */ +@Configuration +class JacksonHttpMessageConvertersConfiguration { + + @Configuration + @ConditionalOnClass(ObjectMapper.class) + @ConditionalOnBean(ObjectMapper.class) + @EnableConfigurationProperties(HttpMapperProperties.class) + @ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "jackson", matchIfMissing = true) + @SuppressWarnings("deprecation") + protected static class MappingJackson2HttpMessageConverterConfiguration { + + // This can be removed when the deprecated class is removed (the ObjectMapper will + // already have all the correct properties). + @Autowired + private HttpMapperProperties properties = new HttpMapperProperties(); + + @Bean + @ConditionalOnMissingBean + public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter( + ObjectMapper objectMapper) { + MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter( + objectMapper); + if (this.properties.isJsonPrettyPrint() != null) { + converter.setPrettyPrint(this.properties.isJsonPrettyPrint()); + } + return converter; + } + + } + + @Configuration + @ConditionalOnClass(XmlMapper.class) + @ConditionalOnBean(Jackson2ObjectMapperBuilder.class) + @EnableConfigurationProperties(HttpMapperProperties.class) + @SuppressWarnings("deprecation") + protected static class MappingJackson2XmlHttpMessageConverterConfiguration { + + @Autowired + private HttpMapperProperties properties = new HttpMapperProperties(); + + @Bean + @ConditionalOnMissingBean + public MappingJackson2XmlHttpMessageConverter mappingJackson2XmlHttpMessageConverter( + Jackson2ObjectMapperBuilder builder) { + MappingJackson2XmlHttpMessageConverter converter = new MappingJackson2XmlHttpMessageConverter(); + converter.setObjectMapper(builder.createXmlMapper(true).build()); + if (this.properties.isJsonPrettyPrint() != null) { + converter.setPrettyPrint(this.properties.isJsonPrettyPrint()); + } + return converter; + } + + } + +} 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 4232d37f46..522fff05d3 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 @@ -130,11 +130,26 @@ public class HttpMessageConvertersAutoConfigurationTests { } @Test - public void gsonIsPreferredWhenBothGsonAndJacksonAreAvailable() { + public void jacksonIsPreferredByDefaultWhenBothGsonAndJacksonAreAvailable() { this.context.register(GsonAutoConfiguration.class, JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class); this.context.refresh(); + assertConverterBeanExists(MappingJackson2HttpMessageConverter.class, + "mappingJackson2HttpMessageConverter"); + assertConverterBeanRegisteredWithHttpMessageConverters(MappingJackson2HttpMessageConverter.class); + assertEquals(0, this.context.getBeansOfType(GsonHttpMessageConverter.class) + .size()); + } + + @Test + public void gsonCanBePreferredWhenBothGsonAndJacksonAreAvailable() { + this.context.register(GsonAutoConfiguration.class, + JacksonAutoConfiguration.class, + HttpMessageConvertersAutoConfiguration.class); + EnvironmentTestUtils.addEnvironment(this.context, + "spring.http.converters.preferred-mapper:gson"); + this.context.refresh(); assertConverterBeanExists(GsonHttpMessageConverter.class, "gsonHttpMessageConverter"); assertConverterBeanRegisteredWithHttpMessageConverters(GsonHttpMessageConverter.class);