Spring Boot中能否配置两个MappingJackson2HttpMessageConverter Bean适配多API需求?
你的配置存在关键问题,直接注册两个MappingJackson2HttpMessageConverter无法实现预期效果
直接在WebMvcConfig里注册两个同类型的消息转换器,Spring MVC会把它们都加入转换器链,处理请求时会按顺序尝试匹配,无法精准区分哪些API用默认转换器、哪些用自定义转换器,最终可能出现所有请求都走第一个转换器的情况,达不到部分API保留null值的目的。
正确的实现思路及方案
方案一:用自定义注解+ControllerAdvice绑定特定转换器
这个方案最灵活,能精准控制哪些接口保留null值:
- 先定义一个标记注解
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface KeepNullFields { }
- 创建ControllerAdvice,给带该注解的接口绑定自定义ObjectMapper
@RestControllerAdvice(annotations = KeepNullFields.class) public class KeepNullResponseBodyAdvice extends AbstractMappingJacksonResponseBodyAdvice { public KeepNullResponseBodyAdvice(@Qualifier("customObjectMapper") ObjectMapper customObjectMapper) { super(customObjectMapper); } }
- 在需要保留null值的控制器或方法上添加注解
@RestController @RequestMapping("/api/need-null") @KeepNullFields public class NeedNullController { @GetMapping("/info") public UserDto getUserInfo() { UserDto dto = new UserDto(); dto.setUsername("张三"); dto.setPhone(null); // 这个null会被保留 return dto; } }
方案二:通过RequestMappingHandlerAdapter针对特定接口配置转换器
如果需要更底层的控制,可以自定义RequestMappingHandlerAdapter,给特定接口绑定转换器:
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Autowired private ObjectMapper defaultObjectMapper; @Autowired @Qualifier("customObjectMapper") private ObjectMapper customObjectMapper; // 注册默认转换器,处理大部分请求 @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { MappingJackson2HttpMessageConverter defaultConverter = new MappingJackson2HttpMessageConverter(defaultObjectMapper); converters.add(defaultConverter); } // 给带@KeepNullFields注解的接口配置自定义转换器 @Bean public RequestMappingHandlerAdapter keepNullRequestMappingHandlerAdapter() { RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter(); MappingJackson2HttpMessageConverter customConverter = new MappingJackson2HttpMessageConverter(customObjectMapper); adapter.setMessageConverters(List.of(customConverter)); // 设置只处理带@KeepNullFields注解的方法 adapter.setRequestMappingHandlerMapping(new RequestMappingHandlerMapping() { @Override protected boolean isHandler(Class<?> beanType) { return beanType.isAnnotationPresent(KeepNullFields.class) || Arrays.stream(beanType.getMethods()).anyMatch(m -> m.isAnnotationPresent(KeepNullFields.class)); } }); return adapter; } }
方案三:接口方法内手动使用自定义ObjectMapper
如果只是少数几个接口需要保留null值,也可以不用全局配置,直接在方法里手动序列化:
@RestController @RequestMapping("/api/single") public class SingleNeedNullController { @Autowired @Qualifier("customObjectMapper") private ObjectMapper customObjectMapper; @GetMapping("/detail") public ResponseEntity<String> getDetail() throws JsonProcessingException { OrderDto dto = new OrderDto(); dto.setOrderNo("20240501001"); dto.setDiscount(null); // 手动用自定义ObjectMapper序列化 String json = customObjectMapper.writeValueAsString(dto); return ResponseEntity.ok(json); } }
额外注意事项
- 你的两个ObjectMapper Bean配置是没问题的,但要确保
customObjectMapper的配置确实开启了保留null值(比如配置serializationInclusion(JsonInclude.Include.ALWAYS)) - 不要直接把两个MappingJackson2HttpMessageConverter都加入全局转换器列表,否则Spring会按顺序匹配,无法区分接口
内容的提问来源于stack exchange,提问作者mohammed rabee
相关产品推荐
相关产品推荐

