使用 Spring Data REST 时如何更改 Jacksons 配置?

Posted

技术标签:

【中文标题】使用 Spring Data REST 时如何更改 Jacksons 配置?【英文标题】:How can I change Jacksons Configuration when using Spring Data REST? 【发布时间】:2015-12-31 16:30:09 【问题描述】:

我正在尝试将 Jackson 配置为以 ISO 8601 格式显示 JSR 310 瞬间。

@Configuration
class Jackson 

    @Bean
    static ObjectMapper objectMapper() 
        ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules();
        objectMapper.disable( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS );
        return objectMapper;
    

然而,这不是一个独特的 Bean,实际上我只想禁用这个设置。所以我并不是真的想创建 ObjectMapper,只是想在上面指定一个设置。

java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.actuate.autoconfigure.EndpointWebMvcHypermediaManagementContextConfiguration$MvcEndpointAdvice': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.fasterxml.jackson.databind.ObjectMapper org.springframework.boot.actuate.autoconfigure.EndpointWebMvcHypermediaManagementContextConfiguration$MvcEndpointAdvice.mapper; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.fasterxml.jackson.databind.ObjectMapper] is defined: expected single matching bean but found 3: objectMapper,halObjectMapper,_halObjectMapper
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:305)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:301)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:196)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:834)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:537)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:667)
at org.springframework.boot.SpringApplication.doRun(SpringApplication.java:342)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:273)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:980)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:969)
at com.xenoterracide.example.Application.main(Application.java:9)
... 5 more
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.fasterxml.jackson.databind.ObjectMapper org.springframework.boot.actuate.autoconfigure.EndpointWebMvcHypermediaManagementContextConfiguration$MvcEndpointAdvice.mapper; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.fasterxml.jackson.databind.ObjectMapper] is defined: expected single matching bean but found 3: objectMapper,halObjectMapper,_halObjectMapper
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:571)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
... 22 more
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.fasterxml.jackson.databind.ObjectMapper] is defined: expected single matching bean but found 3: objectMapper,halObjectMapper,_halObjectMapper
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1079)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:967)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:543)
... 24 more

虽然不是必需的,但这是我的pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.xenoterracide</groupId>
  <artifactId>modern-spring-web-development</artifactId>
  <version>1.0-SNAPSHOT</version>

  <properties>
    <java.version>1.8</java.version>
  </properties>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.3.0.M5</version>
  </parent>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.datatype</groupId>
      <artifactId>jackson-datatype-jsr310</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-rest</artifactId>
    </dependency>
    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
    </dependency>


  </dependencies>

  <repositories>
    <repository>
      <id>spring-milestones</id>
      <name>Spring Milestones</name>
      <url>http://repo.spring.io/milestone</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
  </repositories>

</project>

以及在 Greeting DTO 中具有 Instant 的控制器的输出。

"content":"Hello, \"me\"","time":1443886979.716000000

如何在默认对象映射器上重新配置这一项设置?

【问题讨论】:

【参考方案1】:

您可以(并且可能应该)RepositoryRestConfigurerAdapter(在 Spring Data Rest 2.4 中)或 RepositoryRestMvcConfiguration 公开 configureObjectMapper 方法。

@Configuration
class RepositoryRestAdapterConfiguration extends RepositoryRestConfigurerAdapter 

    @Override
    public void configureJacksonObjectMapper(ObjectMapper objectMapper) 
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.disable( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS );
    


【讨论】:

请注意,这是我能够在 Spring Boot 1.4 中使用的唯一方法 + 以前使用的方法不起作用。【参考方案2】:

实际上,您可以在 application.properties(或 application.yml)中使用 jackson 自动配置属性来使其更具引导性:

spring.jackson.serialization.write_dates_as_timestamps=false

【讨论】:

不错的功能,但我看不到自己想在不同的环境中更改此配置(不像 show-sql 之类的东西)。因此,如果不那么简洁,使用 Java Config 配置它似乎更合适。随着属性的变化似乎也更容易发生意外变化。 我认为使用配置功能可以避免样板代码并专注于业务逻辑而不是低级编码。此外,您不需要显式创建 ObjectMapper bean,spring boot 会为您完成。随时注入即可。 显然我现在没有创建它,我写了一个后续问题,为什么我尝试的方法不起作用。老实说,欢迎您提出这种意见,我喜欢 springs properties 等,对于某些事情,而不是在部署时不应该更改的事情。【参考方案3】:

经过一些实验,我发现如果我用它来注释我的“setter”(setter 注入),它就会运行。这比使用字段注入和@PostConstruct 更简单、更干净。

@Configuration
class Jackson 

   @Autowired
   void configureObjectMapper( final ObjectMapper objectMapper ) 
    objectMapper.disable( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS );
   

【讨论】:

【参考方案4】:

我认为一种方法是将ObjectMapper 注入到合适的bean 类中,然后在@PostConstruct 方法中进行设置,例如:

@Configuration // or @Service or some bean
public class SomeClass ... 

    @Autowired
    private ObjectMapper objectMapper;

    @PostConstruct
    private void configureObjectMapper() 
        objectMapper.disable( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS );
    


【讨论】:

所以我尝试了@Autowired void configureObjectMapper( ObjectMapper mapper ) .... ,但它从未被调用过,为什么这行得通而那行不通? 不知道为什么没有被调用。

以上是关于使用 Spring Data REST 时如何更改 Jacksons 配置?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Spring Data Rest 时如何从组件扫描中排除 @Repository

Spring Data REST 基本路径的更改不涉及自定义控制器

Spring Data Rest:如何使用请求正文发送多部分文件

使用 Spring Data REST 进行密码编码

如何在 Spring-Data-Rest 中实现细粒度的访问控制?

Spring Data Rest PagingAndSortingRepository AbstractMethodError (RepositoryFactorySupport)