Spring Boot在使用@EnableWebMvc时没有使用配置的Jackson ObjectMapper。

4

我想在我的项目中使用预先配置好的 Jackson ObjectMapper(忽略空值和下划线命名法,并使用一些自定义模块)。

在我的大型项目中,我无法使Spring MVC实际使用此映射器。

build.gradle文件:

buildscript {
  ext {
    springBootVersion = '1.5.6.RELEASE'
  }
  repositories {
    mavenCentral()
  }
  dependencies {
    classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
  }
}

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'

version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
  mavenCentral()
}

dependencies {
  compile('org.springframework.boot:spring-boot-starter')
  compile("org.springframework.boot:spring-boot-starter-jetty:${springBootVersion}")
  compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")

  compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.8.8'
  compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.8.8'

  testCompile('org.springframework.boot:spring-boot-starter-test')
}

我的application.yml文件:

spring:
  application:
  name: Jackson test
jackson:
  property-naming-strategy: SNAKE_CASE
  default-property-inclusion: non_empty
debug: true

一个容器类:
public class MyLocationEntity {
    public String nameAndSnake;
}

A config class:

@Configuration
@EnableWebMvc
public class AppConfig {
}

还需要一个控制器:

@RestController
@RequestMapping("/test")
public class TestController {

  @Autowired
  private ObjectMapper objectMapper;

  @RequestMapping(value = "/test", produces = "application/json")
  public MyLocationEntity test() throws JsonProcessingException {
    MyLocationEntity location = new MyLocationEntity();
    location.nameAndSnake = "hello world";
    String expexted = objectMapper.writeValueAsString(location);
    return location;
  }
}

如果我现在在调试器中查看expected的值,它是{"name_and_snake":"hello world"}。 但是如果我让控制器运行,实际响应是{"nameAndSnake":"hello world"}

当我删除@EnableWebMvc时,它可以正常工作。如何在使用配置的映射器与MVC并不删除Web MVC的其余自动配置?


1
为什么要使用EnableWebMvc? - JB Nizet
@JBNizet 这只是演示项目,用于展示错误。在这个项目中它什么也不做。但在更大的项目中是必需的。 - Tom
这完全没有回答我的问题。为什么在更大的项目中需要它?EnableWebMvc 禁用 Web MVC 自动配置。它不会使其启用。请阅读文档:https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-spring-mvc-auto-configuration - JB Nizet
好的,我明白了。所以基本上如果我使用@EnableWebMvc,我会禁用自动配置,因此在application.yml中进行的配置不会生效。 <-- 这将是一个答案,而不是“你为什么要使用它”的问题。 - Tom
1
M. Deinum确实几乎说了这句话,而你却不相信他...但无论如何,至少它让你阅读了文档。 - JB Nizet
显示剩余3条评论
1个回答

9
从 Javadocs 中并不能明显地看出来,但是 @EnableWebMvc 禁用了 Spring Boot 默认的 Web MVC 自动配置,该自动配置由 WebMvcAutoConfiguration 提供,其中包括使用在 application.yml 属性中配置的 Jackson ObjectMapper bean。根据 Spring Boot 参考文档:

9.4.7. 关闭默认的 MVC 配置

完全控制 MVC 配置最简单的方法是提供一个带有 @EnableWebMvc 注解的你自己的 @Configuration。这样做可以使所有的 MVC 配置都在你自己的手中。

因为 @EnableWebMvc 具有禁用自动配置的(可能令人惊讶的)行为,使用此注解可能会产生意想不到和不希望的副作用。更合适的方法可能是采用另一种方式。

话虽如此,仍然可能需要 @EnableWebMvc 的行为。要将 application.yml 属性与 @EnableWebMvc 注解一起使用,必须手动配置 MVC 配置以模仿相关禁用的 Spring Boot 自动配置。有几种不同的方法可以实现这个目的。

第一种方法是复制来自 WebMvcAutoConfiguration.EnableWebMvcConfiguration.configureMessageConverters() 的 Spring Boot 配置代码。 这将替换消息转换器(包括未配置的 ObjectMapperMappingJackson2HttpMessageConverter)。这些消息转换器会使用默认的 Spring Boot 配置中使用的转换器:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private ObjectProvider<HttpMessageConverters> messageConvertersProvider;

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        this.messageConvertersProvider
                .ifAvailable((customConverters) -> converters.addAll(customConverters.getConverters()));
    }
}

除了使用Spring Boot默认的消息转换器列表外,还可以仅替换Spring Boot提供的ObjectMapperMappingJackson2HttpMessageConverter bean(应用了application.yml属性):

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.stream()
                .filter(c -> c instanceof MappingJackson2HttpMessageConverter)
                .map(c -> (MappingJackson2HttpMessageConverter) c)
                .forEach(c -> c.setObjectMapper(objectMapper));

    }
}

或者

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter;

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        for (int i = 0; i < converters.size(); i++) {
            if (converters.get(i) instanceof MappingJackson2HttpMessageConverter) {
                converters.set(i, mappingJackson2HttpMessageConverter);
            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接