如何获取 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】:
ObjectMapper
由Jackson2ObjectMapperBuilder
创建,您可以使用以下方法注入构建器:
@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