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

Posted

技术标签:

【中文标题】如何获取 Spring 4.1 使用的 Jackson ObjectMapper?【英文标题】:How do I obtain the Jackson ObjectMapper in use by Spring 4.1? 【发布时间】:2015-07-15 14:27:18 【问题描述】:

Spring 4.1 实例化了一个 Jackson ObjectMapper 实例。我有理由想将 @Autowire 该实例放入我的一个控制器中:控制器使用 Jackson 对自己的一些次要 JSON 解析,但它使用的 ObjectMapper 应该是 Spring 本身正在使用的同一个实例。我该如何实现呢?

请注意,我不是在问如何自定义配置 Spring 使用的 ObjectMapper;我对默认设置很满意。我只是想把 Spring 使用的实例捞出来,这样我就可以在自己的代码中重用现有的实例。

【问题讨论】:

你确定它创建了一个可以自动装配的bean而不是ObjectMapper的本地实例吗?如果是,这不能从“从 ObjectMapper.class 类型的上下文中加载 bean”中获取吗? 在控制器中添加ObjectMapper 类型的@Autowire 属性是不够的。显然,Spring 并未将其公开为标准 bean。 在测试环境中,我通过在测试类的ObjectMapper@JsonTest 上添加@Autowired 得到它 【参考方案1】:

如果您在类路径上使用 Spring Boot 和 Jackson,并在 REST 控制器中使用 JSON 解析的默认实现,那么这应该可以:

@Autowired
private ObjectMapper jacksonObjectMapper;

【讨论】:

不起作用。使用 Spring-boot 1.4.0.M2 测试,它使用 spring 4.3.0.RC1 并抛出 NoSuchBeanDefinitionException 首先:在组件中自动装配时与 spring boot 1.5.3 一起使用。第二:警告:这是我迄今为止最危险的想法之一。由于它的单例性质,使用 Spring Objectmapper 作为@Autowired bean,您可能会遇到非常丑陋的问题,例如您的控制器中的任何对象都不再被序列化,因为您使用自动装配映射器在任何类中更改了序列化功能(unwrap_root_value,有人吗?)。所以请:当你确定你只需要映射器的 ootb 功能时才使用它。对于所有其他用例,扩展它并创建您自己的自定义映射器 @Dominik 如果我只使用 Springs 自动装配对象映射器来“writeValueAsString”是否安全?我正在处理事务日志,并希望准确捕获输出如何返回给用户。 @afaulconbridge 我经常遇到的一个常见问题是错误地导入了 org.codehaus.jackson.map.ObjectMapper 而不是 com.fasterxml.jackson.databind.ObjectMapper 实际上是 Spring Boot 包从中创建一个 bean。改变这一点,autowired 曾经为我工作。【参考方案2】:

正如其他人所说,您不能将@Autowired 直接放入您的控制器中。

@Emerson Farrugia 建议使用

创建一个新实例
Jackson2ObjectMapperBuilder.json().build()

也对我不起作用,因为获得的实例没有遵循我需要的 spring.jackson.* 配置属性。


我找到的解决方案是从 Spring 的 MappingJackson2HttpMessageConverter 获取 ObjectMapper,它是可注入的。

所以我自动接线:

@Autowired
private MappingJackson2HttpMessageConverter springMvcJacksonConverter;

然后像这样从中获取 ObjectMapper:

ObjectMapper objectMapper = springMvcJacksonConverter.getObjectMapper();

这个实例的行为与 Spring MVC 自己的消息转换完全一样 - 它可能 无论如何都是同一个实例。

【讨论】:

MappingJackson2HttpMessageConverter 不可注入 :( 可注入但objectmapper为空,所以是NPE。 MappingJackson2HttpMessageConverter 似乎也不能在普通的非 Boot Spring 中注入。【参考方案3】:

如果您查看MappingJackson2HttpMessageConverter,您会看到它创建了一个新的ObjectMapper,但并未将其公开为一个bean。有一个吸气剂,但我过去找出它的唯一方法是我自己创建MappingJackson2HttpMessageConverter,例如

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);
    

如果您使用 Spring Boot,则手册中有一个 section 专门用于使用 ObjectMapper 如果您创建默认 Jackson2ObjectMapperBuilder @Bean,您应该能够自动连接相同的 @987654329 @ 控制器中的实例。

【讨论】:

对,我遇到过这条路径,但它涉及重新创建 Spring 创建的默认 Jackson 设置。我不想这样做。 FWIW,Spring 创建了不止一个 ObjectMapper。 JSON 消息转换器创建一个,XML 转换器也是如此。创建与 JSON 消息转换器相同的第三个就像 Jackson2ObjectMapperBuilder.json().build() 一样简单。但是,如果您真的需要它是同一个实例,请在@Bean 中调用该行并将其连接到您需要的任何位置。 Spring 会负责将其连接到转换器中。 为什么弹簧使用不同的 OM 实例。这太令人困惑了。 Spring Boot 文档说 spring.jackson.serialization.indent_output=true 是唯一需要美化 Web 响应的东西。但它确实只是改变了bean,对转换的消息没有影响。 您需要在类声明中添加@Configuration 才能使其工作 文档说,“如果您想完全替换默认的 ObjectMapper,请定义该类型的 @Bean 并将其标记为 @Primary,或者,如果您更喜欢基于构建器的方法,定义一个Jackson2ObjectMapperBuilder@Bean。请注意,在任何一种情况下,这样做都会禁用ObjectMapper的所有自动配置。”因此,创建@Bean 意味着覆盖 yml/properties 文件中的所有设置。如果我想获得配置好的objectmapper怎么办?带有@Autowired 的 Jackson2ObjectMapperBuilder 总是返回 null。【参考方案4】:

ObjectMapperJackson2ObjectMapperBuilder 创建,您可以使用以下方法注入构建器:

@Autowired
private Jackson2ObjectMapperBuilder mapperBuilder;

然后使用mapperBuilder.build()构建一个ObjectMapper实例,这个实例可以使用application.properties中的配置。 Official doc here.

【讨论】:

试图弄清楚我将如何使用该新构建器并将其“安装”到 Spring MVC 默认消息转换中......(我是普通的 Spring MVC,而不是 Spring Boot)。 另外,构建器可以使用mapperBuilder.configure(mapper) 配置现有映射器。这在 Kotlin 中非常有用,您可能希望从 jackson-module-kotlin 的 jacksonObjectMapper() 中获取映射器实例,以获得更好的 Kotlin 支持。【参考方案5】:

我调试了Spring Boot的源代码,发现只有当我们启动整个上下文Jackson2ObjectMapperBuilder时,才会包含我们放入application.yml的配置。

这意味着,如果我们想在 Spring Boot 测试中使用 JUnit 5 生成 ObjectMapper,我们必须:

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

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


    

我们不能只让@SpringBootTest(classes = Jackson2ObjectMapperBuilder.class) 来生成这个构建器。

当我们bootRun 时我们没有这个问题。

配置在Jackson2ObjectMapperBuilderCustomizerConfiguration#customize(Jackson2ObjectMapperBuilder builder) 方法中设置。

【讨论】:

我认为@ExtendWith(SpringExtension.class) 被@SpringBootTest 隐式包含 您可以使用“@JsonTest”测试片来获取配置的映射器,而无需像“@SpringBootTest”那样启动整个应用程序。快多了。见docs.spring.io/spring-boot/docs/current/reference/html/…【参考方案6】:

当您尝试 @Autowire MappingJackson2HttpMessageConverter 时,它会为您提供: No qualifying bean of type 'org.springframework.http.converter.json.MappingJackson2HttpMessageConverter' available: expected single matching bean but found 4: mappingJackson2HttpMessageConverter,jacksonHttpMessageConverter,halJacksonHttpMessageConverter,alpsJsonHttpMessageConverter.

这不是一个大问题,您只需将变量名称更改为上述之一即可获取该实例: @Autowired private MappingJackson2HttpMessageConverter halJacksonHttpMessageConverter;

【讨论】:

【参考方案7】:

如果你愿意,可以分两步;

    在您的 @SpringBootApplication 课程中,添加:

    @Bean
    public ObjectMapper mapper() 
      return new ObjectMapper();
    
    

    任何你想使用的地方ObjectMapper:

    @Autowired
    ObjectMapper mapper;
    

和平!

【讨论】:

这将提供一个 ObjectMapper 实例,但与 Spring 使用的不同。除非您在 2 个地方设置了配置,否则在您的应用程序配置中添加的任何自定义 Jackson 配置都不会在此处存在。

以上是关于如何获取 Spring 4.1 使用的 Jackson ObjectMapper?的主要内容,如果未能解决你的问题,请参考以下文章

Constructor threw exception; nested exception is java.lang.NoClassDefFoundError: com/fasterxml/jacks

Constructor threw exception; nested exception is java.lang.NoClassDefFoundError: com/fasterxml/jacks

如何使用swift 4.1获取url参数的值

Spring 4.1 @JmsListener 配置

如何在 ios 中使用 facebook sdk 4.1 获取用户信息?

spring 配置bean 怎么使用