How can I define a custom ObjectMapper bean without overriding the one used by Spring Boot
Since I didn't want to touch Spring's default ObjectMapper
, creating a @Primary
ObjectMapper
to shadow Spring's default ObjectMapper
was out of the question.
Instead, what I ended up doing is creating a BeanFactoryPostProcessor
which registers in Spring's context a custom, non primary ObjectMapper
:
@Component
public class ObjectMapperPostProcessor implements BeanFactoryPostProcessor {
public static final String OBJECT_MAPPER_BEAN_NAME = "persistenceObjectMapper";
@Override
public void postProcessBeanFactory(final ConfigurableListableBeanFactory beanFactory) {
final AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(ObjectMapper.class, this::getCustomObjectMapper)
.getBeanDefinition();
// Leave Spring's default ObjectMapper (configured by JacksonAutoConfiguration)
// as primary
beanDefinition.setPrimary(false);
final AutowireCandidateQualifier mapperQualifier = new AutowireCandidateQualifier(PersistenceObjectMapper.class);
beanDefinition.addQualifier(mapperQualifier);
((DefaultListableBeanFactory) beanFactory).registerBeanDefinition(OBJECT_MAPPER_BEAN_NAME, beanDefinition);
}
private ObjectMapper getCustomObjectMapper() {
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.setPropertyNamingStrategy(new PropertyNamingStrategy.SnakeCaseStrategy());
return objectMapper;
}
}
As can be seen in the code above, I also assigned a qualifier to my custom ObjectMapper
bean.
My qualifier is an annotation which is annotated with @Qualifier
:
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface PersistenceObjectMapper {
}
I can then autowire my custom ObjectMapper
using my custom annotation, like this:
@Repository
public class MyDao {
@Autowired
public MyDao(DataSource dataSource, @PersistenceObjectMapper ObjectMapper objectMapper) {
// constructor code
}
The Simone Pontiggia answer is in the correct direction. You should create one @Primary bean, which Spring will use in its internals, and then to create your own ObjectMapper beans and autowired them using @Qualifier.
The problem here is that, creating default bean like:
@Bean
@Primary
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
Won't actually work as expected, because the Spring default ObjectMapper has additional configurations. The correct way to create default ObjectMapper that will be used by spring, is:
@Bean
@Primary
public ObjectMapper objectMapper() {
return Jackson2ObjectMapperBuilder.json().build();
}
You can find more information about the Spring default ObjectMapper here: https://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html under 79.3 Customize the Jackson ObjectMapper
You can provide a standard ObjectMapper and your customized object mapper, and set the standard as @Primary
.
Then gives your custom ObjectMapper a name and use it with @Qualifier annotation.
@Configuration
public class Config{
//This bean will be selected for rest
@Bean
@Primary
public ObjectMapper stdMapper(){
return new ObjectMapper();
}
//You can explicitly refer this bean later
@Bean("customObjectMapper")
public ObjectMapper getCustomObjectMapper() {
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.setPropertyNamingStrategy(new PropertyNamingStrategy.SnakeCaseStrategy());
return objectMapper;
}
}
Now you can reference your custom mapper
@Repository
@Transactional
public class MyDaoImpl implements MyDao {
@Autowired
@Qualifier("customObjectMapper")
ObjectMapper objectMapper
//Dao implementation...
}
@Resource("custonmObjectMapper")
will do the same of @Autowired and@Qualifier
together