Register Module beans with Jackson2ObjectMapperBuilder
Prior to this commit, Module beans were registered with all ObjectMapper beans, but were not registered with the auto-configured Jackson2ObjectMapperBuilder. This meant that any ObjectMapper created with the builder but not exposed as a bean would not have the Module beans registered with it. One such ObjectMapper is the one used by the auto-configured MappingJackson2XmlHttpMessageConverter. This caused XML (de)serialization to be different to JSON (de)serialization. This commit updates JacksonAutoConfiguration to register all of the application context's Module beans with the auto-configured Jackson2ObjectMapperBuilder. This ensures consistent configuration of any ObjectMapper that's created using the builder, irrespective of whether or not that ObjectMapper is also exposed as a bean, and also ensures that (de)serialization of JSON and XML is consistent. See gh-2327
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -73,15 +73,15 @@ public class JacksonAutoConfiguration {
|
||||
|
||||
@PostConstruct
|
||||
private void registerModulesWithObjectMappers() {
|
||||
Collection<Module> modules = getBeans(Module.class);
|
||||
for (ObjectMapper objectMapper : getBeans(ObjectMapper.class)) {
|
||||
Collection<Module> modules = getBeans(this.beanFactory, Module.class);
|
||||
for (ObjectMapper objectMapper : getBeans(this.beanFactory, ObjectMapper.class)) {
|
||||
objectMapper.registerModules(modules);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> Collection<T> getBeans(Class<T> type) {
|
||||
return BeanFactoryUtils.beansOfTypeIncludingAncestors(this.beanFactory, type)
|
||||
.values();
|
||||
private static <T> Collection<T> getBeans(ListableBeanFactory beanFactory,
|
||||
Class<T> type) {
|
||||
return BeanFactoryUtils.beansOfTypeIncludingAncestors(beanFactory, type).values();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -131,6 +131,7 @@ public class JacksonAutoConfiguration {
|
||||
configureFeatures(builder, this.jacksonProperties.getGenerator());
|
||||
configureDateFormat(builder);
|
||||
configurePropertyNamingStrategy(builder);
|
||||
configureModules(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
@@ -200,6 +201,12 @@ public class JacksonAutoConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
private void configureModules(Jackson2ObjectMapperBuilder builder) {
|
||||
Collection<Module> moduleBeans = getBeans(this.applicationContext,
|
||||
Module.class);
|
||||
builder.modulesToInstall(moduleBeans.toArray(new Module[moduleBeans.size()]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
|
||||
@@ -19,6 +19,8 @@ package org.springframework.boot.autoconfigure.jackson;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.LocalDateTime;
|
||||
@@ -36,6 +38,7 @@ import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.ObjectCodec;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.MapperFeature;
|
||||
@@ -90,7 +93,8 @@ public class JacksonAutoConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void customJacksonModules() throws Exception {
|
||||
this.context.register(ModulesConfig.class, JacksonAutoConfiguration.class);
|
||||
this.context.register(ModuleConfig.class, MockObjectMapperConfig.class,
|
||||
JacksonAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
ObjectMapper mapper = this.context.getBean(ObjectMapper.class);
|
||||
@SuppressWarnings({ "unchecked", "unused" })
|
||||
@@ -376,13 +380,19 @@ public class JacksonAutoConfigurationTests {
|
||||
SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class ModulesConfig {
|
||||
@Test
|
||||
public void moduleBeansAndWellKnownModulesAreRegisteredWithTheObjectMapperBuilder() {
|
||||
this.context.register(ModuleConfig.class, JacksonAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
ObjectMapper objectMapper = this.context.getBean(
|
||||
Jackson2ObjectMapperBuilder.class).build();
|
||||
assertThat(this.context.getBean(CustomModule.class).getOwners(),
|
||||
hasItem((ObjectCodec) objectMapper));
|
||||
assertThat(objectMapper.canSerialize(LocalDateTime.class), is(true));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Module jacksonModule() {
|
||||
return new SimpleModule();
|
||||
}
|
||||
@Configuration
|
||||
protected static class MockObjectMapperConfig {
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
@@ -392,6 +402,15 @@ public class JacksonAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class ModuleConfig {
|
||||
|
||||
@Bean
|
||||
public CustomModule jacksonModule() {
|
||||
return new CustomModule();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class DoubleModulesConfig {
|
||||
|
||||
@@ -455,4 +474,19 @@ public class JacksonAutoConfigurationTests {
|
||||
this.propertyName = propertyName;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CustomModule extends SimpleModule {
|
||||
|
||||
private Set<ObjectCodec> owners = new HashSet<ObjectCodec>();
|
||||
|
||||
@Override
|
||||
public void setupModule(SetupContext context) {
|
||||
this.owners.add(context.getOwner());
|
||||
}
|
||||
|
||||
Set<ObjectCodec> getOwners() {
|
||||
return this.owners;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user