如何在不覆盖 Spring Boot 使用的情况下定义自定义 ObjectMapper bean
Posted
技术标签:
【中文标题】如何在不覆盖 Spring Boot 使用的情况下定义自定义 ObjectMapper bean【英文标题】:How can I define a custom ObjectMapper bean without overriding the one used by Spring Boot 【发布时间】: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 进行自己的内部工作?
【问题讨论】:
【参考方案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?)。
【讨论】:
以上是关于如何在不覆盖 Spring Boot 使用的情况下定义自定义 ObjectMapper bean的主要内容,如果未能解决你的问题,请参考以下文章
Spring Boot - 如何在不使用 spring 注释的情况下在运行时获取端口
如何在不从 spring-boot-starter-web 继承的情况下在 Spring Boot 中获取 ObjectMapper 实例?
如何在不使用 Spring Boot 的情况下注入 Feign Client 并调用 REST Endpoint
如何在不依赖 MongoDB 的情况下启动 spring-boot 应用程序?