为什么 Spring Boot 不使用 @Primary Jackson ObjectMapper 在 rest 控制器上进行 JSON 序列化?
Why is Spring Boot not using a @Primary Jackson ObjectMapper for JSON serialization on a rest controller?
我有一个 class 设置为 return 一个自定义的 ObjectMapper。据我所知,Spring Boot 使用此 ObjectMapper 的正确方法是将其声明为 @Primary,它是。
@Configuration
public class MyJacksonConfiguration {
@Bean
@Primary
public ObjectMapper objectMapper() {
return Jackson2ObjectMapperBuilder
.json()
.findModulesViaServiceLoader(true)
.mixIn(Throwable.class, ThrowableMixin.class)
.featuresToDisable(
WRITE_DATES_AS_TIMESTAMPS)
.serializationInclusion(
Include.NON_ABSENT)
.build();
}
}
然而,当我 return 来自控制器方法的对象时,它使用默认的 Jackson ObjectMapper 配置进行序列化。
如果我向我的控制器添加一个明确的 ObjectMapper 并对其调用 writeValueAsString,我可以看到这个 ObjectMapper 是我希望 Spring 启动时使用的自定义对象。
@RestController
public class TestController {
@Autowired
private TestService service;
@Autowired
private ObjectMapper mapper;
@GetMapping(value = "/test", produces = "application/json")
public TestResult getResult() {
final TestResult ret = service.getResult();
String test = "";
try {
test = mapper.writeValueAsString(ret);
// test now contains the value I'd like returned by the controller!
} catch (final JsonProcessingException e) {
e.printStackTrace();
}
return ret;
}
}
当我 运行 在我的控制器上进行测试时,测试 class 也使用自动装配的 ObjectMapper。同样,提供给测试的 ObjectMapper 是自定义的。
所以 Spring 在某种程度上知道自定义的 ObjectMapper,但我的其余控制器没有使用它 classes。
我已尝试为 Spring 打开调试日志记录,但在日志中看不到任何有用的信息。
知道可能会发生什么,或者我还应该去哪里寻找问题?
编辑:似乎有多种方法可以做到这一点,但我尝试这样做的方式似乎是一种推荐的方法,我想得到它以这种方式工作 - 参见 https://docs.spring.io/spring-boot/docs/1.4.7.RELEASE/reference/html/howto-spring-mvc.html#howto-customize-the-jackson-objectmapper 的 71.3 - 我是不是误解了什么?
Spring 使用 HttpMessageConverters 呈现@ResponseBody(或来自@RestController 的响应)。我认为您需要覆盖 HttpMessageConverter。
您可以通过扩展 WebMvcConfigurerAdapter
并覆盖以下内容来做到这一点。
@Override
public void configureMessageConverters(
List<HttpMessageConverter<?>> converters) {
messageConverters.add(new MappingJackson2HttpMessageConverter());
super.configureMessageConverters(converters);
}
因为您所做的是创建一个 bean,您可以 Autowired
供以后使用。 (就像你在代码中写的那样)Spring boot 使用 auto-configured Jackson2ObjectMapperBuilder
获取默认的 ObjectMapper 进行序列化。
所以如果你想在你的rest controller上自定义一个ObjectMapper
用于序列化,你需要想办法配置Jackson2ObjectMapperBuilder
。有两种方法可以做到这一点:
为 spring
声明您自己的 Jackson2ObjectMapperBuilder
@Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
// other settings you want.
return builder;
}
通过属性设置
可以通过application.properties
设置。例如,如果要启用漂亮打印,请设置 spring.jackson.serialization.indent_output=true
.
更多详细信息,请查看这里:
Customize the Jackson ObjectMapper
虽然其他答案显示了实现相同结果的替代方法,但这个问题的实际答案是我定义了一个扩展 WebMvcConfigurationSupport
的单独 class。通过这样做,WebMvcAutoConfiguration
bean 已被禁用,因此 @Primary ObjectMapper 未被 Spring 拾取。 (在 WebMvcAutoConfiguration
source 中寻找 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
。)
暂时删除扩展 WebMvcConfigurationSupport
的 class 允许 @Primary
ObjectMapper
按预期被 Spring 拾取和使用。
由于无法永久删除扩展 class 的 WebMvcConfigurationSupport
,因此我向其中添加了以下内容:
@Autowired
private ObjectMapper mapper;
@Override
public void configureMessageConverters(final List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJackson2HttpMessageConverter(mapper));
addDefaultHttpMessageConverters(converters);
super.configureMessageConverters(converters);
}
不会破坏字符串序列化的正确方法(保持顺序):
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
addDefaultHttpMessageConverters(converters);
HttpMessageConverter<?> jacksonConverter =
converters.stream()
.filter(converter -> converter.getClass().equals(MappingJackson2HttpMessageConverter.class))
.findFirst().orElseThrow(RuntimeException::new);
converters.add(converters.indexOf(jacksonConverter), new MappingJackson2HttpMessageConverter(objectMapper));
}
我有一个 class 设置为 return 一个自定义的 ObjectMapper。据我所知,Spring Boot 使用此 ObjectMapper 的正确方法是将其声明为 @Primary,它是。
@Configuration
public class MyJacksonConfiguration {
@Bean
@Primary
public ObjectMapper objectMapper() {
return Jackson2ObjectMapperBuilder
.json()
.findModulesViaServiceLoader(true)
.mixIn(Throwable.class, ThrowableMixin.class)
.featuresToDisable(
WRITE_DATES_AS_TIMESTAMPS)
.serializationInclusion(
Include.NON_ABSENT)
.build();
}
}
然而,当我 return 来自控制器方法的对象时,它使用默认的 Jackson ObjectMapper 配置进行序列化。
如果我向我的控制器添加一个明确的 ObjectMapper 并对其调用 writeValueAsString,我可以看到这个 ObjectMapper 是我希望 Spring 启动时使用的自定义对象。
@RestController
public class TestController {
@Autowired
private TestService service;
@Autowired
private ObjectMapper mapper;
@GetMapping(value = "/test", produces = "application/json")
public TestResult getResult() {
final TestResult ret = service.getResult();
String test = "";
try {
test = mapper.writeValueAsString(ret);
// test now contains the value I'd like returned by the controller!
} catch (final JsonProcessingException e) {
e.printStackTrace();
}
return ret;
}
}
当我 运行 在我的控制器上进行测试时,测试 class 也使用自动装配的 ObjectMapper。同样,提供给测试的 ObjectMapper 是自定义的。
所以 Spring 在某种程度上知道自定义的 ObjectMapper,但我的其余控制器没有使用它 classes。
我已尝试为 Spring 打开调试日志记录,但在日志中看不到任何有用的信息。
知道可能会发生什么,或者我还应该去哪里寻找问题?
编辑:似乎有多种方法可以做到这一点,但我尝试这样做的方式似乎是一种推荐的方法,我想得到它以这种方式工作 - 参见 https://docs.spring.io/spring-boot/docs/1.4.7.RELEASE/reference/html/howto-spring-mvc.html#howto-customize-the-jackson-objectmapper 的 71.3 - 我是不是误解了什么?
Spring 使用 HttpMessageConverters 呈现@ResponseBody(或来自@RestController 的响应)。我认为您需要覆盖 HttpMessageConverter。
您可以通过扩展 WebMvcConfigurerAdapter
并覆盖以下内容来做到这一点。
@Override
public void configureMessageConverters(
List<HttpMessageConverter<?>> converters) {
messageConverters.add(new MappingJackson2HttpMessageConverter());
super.configureMessageConverters(converters);
}
因为您所做的是创建一个 bean,您可以 Autowired
供以后使用。 (就像你在代码中写的那样)Spring boot 使用 auto-configured Jackson2ObjectMapperBuilder
获取默认的 ObjectMapper 进行序列化。
所以如果你想在你的rest controller上自定义一个ObjectMapper
用于序列化,你需要想办法配置Jackson2ObjectMapperBuilder
。有两种方法可以做到这一点:
为 spring
声明您自己的 Jackson2ObjectMapperBuilder@Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
// other settings you want.
return builder;
}
通过属性设置
可以通过application.properties
设置。例如,如果要启用漂亮打印,请设置 spring.jackson.serialization.indent_output=true
.
更多详细信息,请查看这里: Customize the Jackson ObjectMapper
虽然其他答案显示了实现相同结果的替代方法,但这个问题的实际答案是我定义了一个扩展 WebMvcConfigurationSupport
的单独 class。通过这样做,WebMvcAutoConfiguration
bean 已被禁用,因此 @Primary ObjectMapper 未被 Spring 拾取。 (在 WebMvcAutoConfiguration
source 中寻找 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
。)
暂时删除扩展 WebMvcConfigurationSupport
的 class 允许 @Primary
ObjectMapper
按预期被 Spring 拾取和使用。
由于无法永久删除扩展 class 的 WebMvcConfigurationSupport
,因此我向其中添加了以下内容:
@Autowired
private ObjectMapper mapper;
@Override
public void configureMessageConverters(final List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJackson2HttpMessageConverter(mapper));
addDefaultHttpMessageConverters(converters);
super.configureMessageConverters(converters);
}
不会破坏字符串序列化的正确方法(保持顺序):
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
addDefaultHttpMessageConverters(converters);
HttpMessageConverter<?> jacksonConverter =
converters.stream()
.filter(converter -> converter.getClass().equals(MappingJackson2HttpMessageConverter.class))
.findFirst().orElseThrow(RuntimeException::new);
converters.add(converters.indexOf(jacksonConverter), new MappingJackson2HttpMessageConverter(objectMapper));
}