如何在不覆盖 Spring Boot 使用的对象的情况下定义自定义 ObjectMapper bean

Dor*_*old 7 java jackson spring-boot

我有一个包含多个@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;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

@Repository
@Transactional
public class MyDaoImpl implements MyDao {

@Autowired
ObjectMapper objectMapper

//Dao implementation...

}
Run Code Online (Sandbox Code Playgroud)

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

我如何告诉 Spring Boot检测也不将我的自定义ObjectMapperbean 用于其内部工作?

mar*_*kov 8

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

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

@Bean
@Primary
public ObjectMapper objectMapper() {
    return new ObjectMapper();
}
Run Code Online (Sandbox Code Playgroud)

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

@Bean
@Primary
public ObjectMapper objectMapper() {
    return Jackson2ObjectMapperBuilder.json().build();
}
Run Code Online (Sandbox Code Playgroud)

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


Dor*_*old 5

由于我不想触及 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;
}
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

@Repository
public class MyDao {
@Autowired
public MyDao(DataSource dataSource, @PersistenceObjectMapper ObjectMapper objectMapper) {
// constructor code
}
Run Code Online (Sandbox Code Playgroud)