【问题标题】:How can I define a custom ObjectMapper bean without overriding the one used by Spring Boot如何在不覆盖 Spring Boot 使用的情况下定义自定义 ObjectMapper bean
【发布时间】:2018-08-14 18:56:21
【问题描述】:

我有一个带有多个 @RestController 类的 Spring Boot Web 应用程序。 我喜欢我的 REST 控制器返回的默认 json 格式。

为了在我的 DAO bean(进行 json 序列化和反序列化)中使用,我创建了一个自定义 ObjectMapper

@Configuration
public class Config{

  @Bean
  public ObjectMapper getCustomObjectMapper() {
    final ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    objectMapper.setPropertyNamingStrategy(new PropertyNamingStrategy.SnakeCaseStrategy());
    return objectMapper;
  }
}

在我的每个 DAO 类中,我都会自动装配我的自定义 ObjectMapper

@Repository
@Transactional
public class MyDaoImpl implements MyDao {

@Autowired
ObjectMapper objectMapper

//Dao implementation...

}

这一切都很好。问题是我的自定义 ObjectMapper 被 Spring 自动拾取并用于序列化 REST 响应。
这是不可取的。对于 REST 控制器,我想保留 Spring 默认创建的 ObjectMapper

我如何告诉 Spring Boot 检测和 使用我的自定义 ObjectMapper bean 进行自己的内部工作?

【问题讨论】:

    标签: java spring-boot jackson


    【解决方案1】:

    您可以提供一个标准的ObjectMapper和您自定义的对象映射器,并将标准设置为@Primary

    然后为您的自定义 ObjectMapper 命名,并将其与 @Qualifier 注释一起使用。

    @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;
      }
    }
    

    现在您可以引用您的自定义映射器

    @Repository
    @Transactional
    public class MyDaoImpl implements MyDao {
    
    @Autowired
    @Qualifier("customObjectMapper")
    ObjectMapper objectMapper
    
    //Dao implementation...
    
    }
    

    @Resource("custonmObjectMapper") 将与 @Autowired 和 @Qualifier 一起做同样的事情

    【讨论】:

      【解决方案2】:

      因为我不想触及 Spring 的默认 ObjectMapper,所以创建一个 @Primary ObjectMapper 来影响 Spring 的默认 ObjectMapper 是不可能的。

      相反,我最终做的是创建一个 BeanFactoryPostProcessor,它在 Spring 的上下文中注册一个自定义的非主要 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;
      }
      }
      

      从上面的代码中可以看出,我还为我的自定义 ObjectMapper bean 分配了一个限定符。
      我的限定符是一个用@Qualifier 注释的注释:

      @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE })
      @Retention(RetentionPolicy.RUNTIME)
      @Qualifier
      public @interface PersistenceObjectMapper {
      }
      

      然后我可以使用我的自定义注释自动连接我的自定义 ObjectMapper,如下所示:

      @Repository
      public class MyDao {
      @Autowired
      public MyDao(DataSource dataSource, @PersistenceObjectMapper ObjectMapper objectMapper) {
      // constructor code
      }
      

      【讨论】:

        【解决方案3】:

        Simone Pontiggia 的答案是正确的。您应该创建一个 @Primary bean,Spring 将在其内部使用它,然后创建您自己的 ObjectMapper bean 并使用 @Qualifier 自动装配它们。

        这里的问题是,创建默认 bean 如下:

        @Bean
        @Primary
        public ObjectMapper objectMapper() {
            return new ObjectMapper();
        }
        

        实际上不会按预期工作,因为 Spring 默认的 ObjectMapper 有额外的配置。 创建spring将使用的默认ObjectMapper的正确方法是:

        @Bean
        @Primary
        public ObjectMapper objectMapper() {
            return Jackson2ObjectMapperBuilder.json().build();
        }
        

        您可以在此处找到有关 Spring 默认 ObjectMapper 的更多信息:https://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html under 79.3 Customize the Jackson ObjectMapper

        【讨论】:

          【解决方案4】:

          您可以创建:

          public class MapperUtils {
          
              private static final ObjectMapper mapper = new ObjectMapper();
          
              public static <T> T parseResponse(byte[] byteArrray, Class<T> parseType) throws JsonParseException, JsonMappingException, IOException {
                  return mapper.readValue(byteArrray, parseType);
              }
          }
          

          ObjectMapper 是线程安全的。但是,由于性能问题,有些人不鼓励使用单实例 (Should I declare Jackson's ObjectMapper as a static field?)。

          【讨论】:

            猜你喜欢
            • 2021-07-27
            • 1970-01-01
            • 2023-04-04
            • 2019-08-13
            • 1970-01-01
            • 2012-05-15
            • 1970-01-01
            • 1970-01-01
            • 2020-07-08
            相关资源
            最近更新 更多