Make it easier to use Gson and the Actuator at the same time

Previously the recommendation when a user wanted to use Gson was to
exclude Jackson from the classpath and add Gson. This worked fine until
the user also want to use the Actuator which requires Jackson. To get
over this hurdle the user could leave Jackson on the classpath and
perform their own HttpMessageConverter configuration and register a
GsonHttpMessageConverter instead of or before any Jackson-based
converter. A little complicated, but it worked.

This commit makes things easier by updating the auto-configuration for
HTTP message converters to prefer Gson when both Gson and Jackson are
on the classpath, i.e. in the presence of both, a
GsonHttpMessageConverter will be auto-configured and a
MappingJackson2HttpMessageConverter won’t be. This allows an
application to easily use Gson while allowing the Actuator to continue
to use Jackson.

Closes gh-2247
This commit is contained in:
Andy Wilkinson 2015-02-04 17:34:14 +00:00
parent 68c7c65d52
commit d718a80316
2 changed files with 25 additions and 17 deletions

View File

@ -25,7 +25,7 @@ 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.condition.ConditionalOnMissingClass;
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;
@ -55,7 +55,7 @@ import com.google.gson.Gson;
*/
@Configuration
@ConditionalOnClass(HttpMessageConverter.class)
@AutoConfigureAfter(JacksonAutoConfiguration.class)
@AutoConfigureAfter({ GsonAutoConfiguration.class, JacksonAutoConfiguration.class })
public class HttpMessageConvertersAutoConfiguration {
@Autowired(required = false)
@ -70,6 +70,7 @@ public class HttpMessageConvertersAutoConfiguration {
@Configuration
@ConditionalOnClass(ObjectMapper.class)
@ConditionalOnBean(ObjectMapper.class)
@ConditionalOnMissingBean(GsonHttpMessageConverter.class)
@EnableConfigurationProperties(HttpMapperProperties.class)
@SuppressWarnings("deprecation")
protected static class MappingJackson2HttpMessageConverterConfiguration {
@ -119,7 +120,6 @@ public class HttpMessageConvertersAutoConfiguration {
@Configuration
@ConditionalOnClass(Gson.class)
@ConditionalOnMissingClass(name = "com.fasterxml.jackson.core.JsonGenerator")
@ConditionalOnBean(Gson.class)
protected static class GsonHttpMessageConverterConfiguration {

View File

@ -22,6 +22,8 @@ import java.util.List;
import org.junit.After;
import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
@ -118,17 +120,32 @@ public class HttpMessageConvertersAutoConfigurationTests {
@Test
public void defaultGsonConverter() throws Exception {
this.context.register(GsonConfig.class,
this.context.register(GsonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class);
this.context.refresh();
// Shouldn't be registered because we have Jackson
assertEquals(0, this.context.getBeansOfType(GsonHttpMessageConverter.class)
.size());
assertConverterBeanExists(GsonHttpMessageConverter.class,
"gsonHttpMessageConverter");
assertConverterBeanRegisteredWithHttpMessageConverters(GsonHttpMessageConverter.class);
}
@Test
public void gsonIsPreferredWhenBothGsonAndJacksonAreAvailable() {
this.context.register(GsonAutoConfiguration.class,
JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class);
this.context.refresh();
assertConverterBeanExists(GsonHttpMessageConverter.class,
"gsonHttpMessageConverter");
assertConverterBeanRegisteredWithHttpMessageConverters(GsonHttpMessageConverter.class);
assertEquals(0,
this.context.getBeansOfType(MappingJackson2HttpMessageConverter.class)
.size());
}
@Test
public void customGsonConverter() throws Exception {
this.context.register(GsonConfig.class, GsonConverterConfig.class,
this.context.register(GsonAutoConfiguration.class, GsonConverterConfig.class,
HttpMessageConvertersAutoConfiguration.class);
this.context.refresh();
assertConverterBeanExists(GsonHttpMessageConverter.class,
@ -227,15 +244,6 @@ public class HttpMessageConvertersAutoConfigurationTests {
}
}
@Configuration
protected static class GsonConfig {
@Bean
public Gson gson() {
return new Gson();
}
}
@Configuration
protected static class GsonConverterConfig {