如何获得 Spring 4.1使用的 Jackson ObjectMapper?

Spring 4.1实例化 Jackson ObjectMapper实例。我有理由希望将 @Autowire实例放到我的一个控制器中: 控制器使用 Jackson 对它自己的 JSON 进行一些次要的解析,但它使用的 ObjectMapper应该是 Spring 本身正在使用的实例。我该怎么做呢?

请注意,我并没有询问如何自定义配置 Spring 使用的 ObjectMapper; 我对默认值感到满意。我只是想找出 Spring 使用的实例,这样我就可以在自己的代码中重用现有的实例。

115849 次浏览

If you take a look at MappingJackson2HttpMessageConverter, you'll see that it creates a new ObjectMapper, but doesn't expose it as a bean. There's a getter, but the only way I've fished it out in the past is when I created the MappingJackson2HttpMessageConverter myself, e.g.

public class WebMvcConfiguration extends WebMvcConfigurerAdapter {


@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {


MappingJackson2HttpMessageConverter jacksonMessageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = jacksonMessageConverter.getObjectMapper();


objectMapper.registerModule(new JodaModule());
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);


converters.add(jacksonMessageConverter);
}
}

If you're working with Spring Boot, there's a section in the manual dedicated to working with the ObjectMapper If you create a default Jackson2ObjectMapperBuilder @Bean, you should be able to autowire that same ObjectMapper instance in your controller.

If you're using Spring Boot with Jackson on your classpath and default implementation for JSON parsing in your REST controller, then this should work:

@Autowired
private ObjectMapper jacksonObjectMapper;

As was said by others, you can't @Autowired it in directly into your controller.

@Emerson Farrugia's suggestion to create a new instance using

Jackson2ObjectMapperBuilder.json().build()

also didn't work for me because the obtained instance was not following the spring.jackson.* configuration properties, which I needed it to.


The solution I found was to obtain the ObjectMapper from Spring's MappingJackson2HttpMessageConverter which is injectable.

So I autowired it:

@Autowired
private MappingJackson2HttpMessageConverter springMvcJacksonConverter;

and then get the ObjectMapper from it like this:

ObjectMapper objectMapper = springMvcJacksonConverter.getObjectMapper();

This instance behaves exactly as Spring MVC's own message conversion - it probably is the same instance anyway.

A two-stepper if you will;

  1. At your @SpringBootApplication class, add:

    @Bean
    public ObjectMapper mapper() {
    return new ObjectMapper();
    }
    
  2. Anywhere you wish to use ObjectMapper:

    @Autowired
    ObjectMapper mapper;
    

Peace!

When you try to @Autowire the MappingJackson2HttpMessageConverter it gives you: No qualifying bean of type 'org.springframework.http.converter.json.MappingJackson2HttpMessageConverter' available: expected single matching bean but found 4: mappingJackson2HttpMessageConverter,jacksonHttpMessageConverter,halJacksonHttpMessageConverter,alpsJsonHttpMessageConverter.

This is not a big issue, you can just change your variable name to one of the above to get that instance: @Autowired private MappingJackson2HttpMessageConverter halJacksonHttpMessageConverter;

The ObjectMapper is created by Jackson2ObjectMapperBuilder, and you can inject the builder using:

@Autowired
private Jackson2ObjectMapperBuilder mapperBuilder;

Then use mapperBuilder.build() to build an ObjectMapper instance, and this instance can use configurations in application.properties. Official doc here.

I have debugged into source code of Spring Boot and found that only when we launch the whole context Jackson2ObjectMapperBuilder will contain the config we put in application.yml.

This means, if we want to generate an ObjectMapper in a Spring Boot test, with JUnit 5, we have to:

@ExtendWith(SpringExtension.class)
@SpringBootTest
class SomeTest {
@Autowired
private Jackson2ObjectMapperBuilder builder;
...


@Test
void testObjectMapper() {
ObjectMapper mapper = builder.build();




}

We cannot only make @SpringBootTest(classes = Jackson2ObjectMapperBuilder.class) to generate this builder.

When we bootRun we don't have this problem.

The configuration is set in Jackson2ObjectMapperBuilderCustomizerConfiguration#customize(Jackson2ObjectMapperBuilder builder) method.

enter image description here

An Update for Spring 4.3:

  • As of Spring 4.3, classes with a single constructor can omit the @Autowired annotation
  • Consequently, if you add a constructor which takes an ObjectMapper parameter it will be autowired with the Jackson ObjectMapper instance without needing to use the annotation @Autowired