Spring MVC

Spring Boot 有多个包含 Spring MVC 的启动器。 请注意,一些启动器包含对 Spring MVC 的依赖,而不是直接包含它。 本节回答有关 Spring MVC 和 Spring Boot 的常见问题。

编写 JSON REST 服务

只要 Jackson2 在类路径上,Spring Boot 应用程序中的任何 Spring @RestController 都应该默认渲染 JSON 响应,如下例所示:

  • Java

  • Kotlin

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {

	@RequestMapping("/thing")
	public MyThing thing() {
		return new MyThing();
	}

}
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class MyController {

	@RequestMapping("/thing")
	fun thing(): MyThing {
		return MyThing()
	}

}

只要 MyThing 可以被 Jackson2 序列化(对于普通的 POJO 或 Groovy 对象来说都是如此),那么 localhost:8080/thing 默认会提供它的 JSON 表示。 请注意,在浏览器中,您有时可能会看到 XML 响应,因为浏览器倾向于发送优先选择 XML 的 accept 头。

编写 XML REST 服务

如果您的类路径上有 Jackson XML 扩展(jackson-dataformat-xml),您可以使用它来渲染 XML 响应。 我们用于 JSON 的前一个示例也可以工作。 要使用 Jackson XML 渲染器,请将以下依赖项添加到您的项目中:

<dependency>
	<groupId>com.fasterxml.jackson.dataformat</groupId>
	<artifactId>jackson-dataformat-xml</artifactId>
</dependency>

如果 Jackson 的 XML 扩展不可用,而 JAXB 可用,则可以通过将 MyThing 注解为 @XmlRootElement 来渲染 XML,如下例所示:

  • Java

  • Kotlin

import jakarta.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class MyThing {

	private String name;

	// getters/setters ...

	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}

}
import jakarta.xml.bind.annotation.XmlRootElement

@XmlRootElement
class MyThing {

	var name: String? = null

}

您需要确保 JAXB 库是项目的一部分,例如通过添加:

<dependency>
	<groupId>org.glassfish.jaxb</groupId>
	<artifactId>jaxb-runtime</artifactId>
</dependency>

注意:要让服务器渲染 XML 而不是 JSON,您可能需要发送 Accept: text/xml 头(或使用浏览器)。

自定义 Jackson ObjectMapper

Spring MVC(客户端和服务器端)使用 HttpMessageConverters 来协商 HTTP 交换中的内容转换。 如果 Jackson 在类路径上,您已经获得了由 Jackson2ObjectMapperBuilder 提供的默认转换器,该构建器的实例会自动为您配置。

ObjectMapper(或用于 Jackson XML 转换器的 XmlMapper)实例(默认创建)具有以下自定义属性:

  • MapperFeature.DEFAULT_VIEW_INCLUSION 被禁用

  • DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES 被禁用

  • SerializationFeature.WRITE_DATES_AS_TIMESTAMPS 被禁用

  • SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS 被禁用

Spring Boot 还有一些功能可以更容易地自定义此行为。

您可以使用环境配置 ObjectMapperXmlMapper 实例。 Jackson 提供了一套广泛的开/关功能,可用于配置其处理的各个方面。 这些功能在 Jackson 中的几个枚举中描述,这些枚举映射到环境中的属性:

枚举 属性

EnumFeature

spring.jackson.datatype.enum.<feature_name>

true, false

JsonNodeFeature

spring.jackson.datatype.json-node.<feature_name>

true, false

DeserializationFeature

spring.jackson.deserialization.<feature_name>

true, false

JsonGenerator.Feature

spring.jackson.generator.<feature_name>

true, false

MapperFeature

spring.jackson.mapper.<feature_name>

true, false

JsonParser.Feature

spring.jackson.parser.<feature_name>

true, false

SerializationFeature

spring.jackson.serialization.<feature_name>

true, false

JsonInclude.Include

spring.jackson.default-property-inclusion

always, non_null, non_absent, non_default, non_empty

例如,要启用漂亮打印,请设置 spring.jackson.serialization.indent_output=true。 请注意,由于使用了 宽松绑定indent_output 的大小写不必与相应枚举常量的大小写匹配,即 INDENT_OUTPUT

这种基于环境的配置应用于自动配置的 Jackson2ObjectMapperBuilder bean,并适用于使用构建器创建的任何映射器,包括自动配置的 ObjectMapper bean。

上下文的 Jackson2ObjectMapperBuilder 可以通过一个或多个 Jackson2ObjectMapperBuilderCustomizer bean 进行自定义。 这些自定义器 bean 可以排序(Boot 自己的自定义器顺序为 0),允许在 Boot 的自定义之前和之后应用额外的自定义。

任何类型为 Module 的 bean 都会自动注册到自动配置的 Jackson2ObjectMapperBuilder 中,并应用于它创建的任何 ObjectMapper 实例。 这为在向应用程序添加新功能时贡献自定义模块提供了一种全局机制。

如果您想完全替换默认的 ObjectMapper,可以定义该类型的 @Bean,或者如果您更喜欢基于构建器的方法,可以定义 Jackson2ObjectMapperBuilder @Bean。 在定义 ObjectMapper bean 时,建议将其标记为 @Primary,因为它将替换的自动配置的 ObjectMapper@Primary。 请注意,无论哪种情况,这样做都会禁用 ObjectMapper 的所有自动配置。

如果您提供任何类型为 MappingJackson2HttpMessageConverter@Beans,它们会替换 MVC 配置中的默认值。 此外,还提供了一个类型为 HttpMessageConverters 的便捷 bean(如果您使用默认的 MVC 配置,它始终可用)。 它有一些有用的方法来访问默认和用户增强的消息转换器。

有关更多详细信息,请参阅 自定义 @ResponseBody 渲染 部分和 WebMvcAutoConfiguration 源代码。

自定义 @ResponseBody 渲染

Spring 使用 HttpMessageConverters 来渲染 @ResponseBody(或来自 @RestController 的响应)。 您可以通过在 Spring Boot 上下文中添加适当类型的 bean 来贡献额外的转换器。 如果您添加的 bean 的类型本来就会被默认包含(例如用于 JSON 转换的 MappingJackson2HttpMessageConverter),它会替换默认值。 如果您使用默认的 MVC 配置,还提供了一个类型为 HttpMessageConverters 的便捷 bean,它始终可用。 它有一些有用的方法来访问默认和用户增强的消息转换器(例如,如果您想手动将它们注入到自定义的 RestTemplate 中,这可能很有用)。

与普通 MVC 用法一样,您提供的任何 WebMvcConfigurer bean 也可以通过覆盖 configureMessageConverters 方法来贡献转换器。 但是,与普通 MVC 不同,您只能提供您需要的额外转换器(因为 Spring Boot 使用相同的机制来贡献其默认值)。 最后,如果您通过提供自己的 @EnableWebMvc 配置来选择退出默认的 Spring Boot MVC 配置,您可以通过使用 WebMvcConfigurationSupportgetMessageConverters 完全控制并手动完成所有操作。

有关更多详细信息,请参阅 WebMvcAutoConfiguration 源代码。

处理多部分文件上传

Spring Boot 采用 servlet 5 Part API 来支持文件上传。 默认情况下,Spring Boot 配置 Spring MVC 时,每个文件的最大大小为 1MB,单个请求中的文件数据最大为 10MB。 您可以通过使用 MultipartProperties 类中公开的属性来覆盖这些值、中间数据存储的位置(例如,到 /tmp 目录)以及数据刷新到磁盘的阈值。 例如,如果您想指定文件大小不受限制,请将 spring.servlet.multipart.max-file-size 属性设置为 -1

当您想在 Spring MVC 控制器处理方法中接收多部分编码的文件数据作为类型为 MultipartFile@RequestParam 注解参数时,多部分支持很有帮助。

有关更多详细信息,请参阅 MultipartAutoConfiguration 源代码。

注意:建议使用容器的内置多部分上传支持,而不是引入额外的依赖项,如 Apache Commons File Upload。

关闭 Spring MVC DispatcherServlet

默认情况下,所有内容都从应用程序的根目录(/)提供服务。 如果您想映射到不同的路径,可以按如下方式配置:

  • Properties

  • YAML

spring.mvc.servlet.path=/mypath
spring:
  mvc:
    servlet:
      path: "/mypath"

如果您有其他 servlet,您可以为每个 servlet 声明一个类型为 ServletServletRegistrationBean@Bean,Spring Boot 将透明地将它们注册到容器中。 因为 servlet 是以这种方式注册的,所以它们可以映射到 DispatcherServlet 的子上下文,而不会调用它。

自己配置 DispatcherServlet 是不常见的,但如果您确实需要这样做,还必须提供一个类型为 DispatcherServletPath@Bean 来提供自定义 DispatcherServlet 的路径。

关闭默认 MVC 配置

完全控制 MVC 配置的最简单方法是提供您自己的带有 @EnableWebMvc 注解的 @Configuration。 这样做会将所有 MVC 配置交给您处理。

自定义 ViewResolvers

ViewResolver 是 Spring MVC 的核心组件,它将 @Controller 中的视图名称转换为实际的 View 实现。 请注意,视图解析器主要用于 UI 应用程序,而不是 REST 风格的服务(View 不用于渲染 @ResponseBody)。 有许多 ViewResolver 的实现可供选择,Spring 本身对您应该使用哪些实现没有意见。 另一方面,Spring Boot 会根据它在类路径和应用程序上下文中找到的内容为您安装一个或两个。 DispatcherServlet 使用它在应用程序上下文中找到的所有解析器,依次尝试每个解析器,直到获得结果。 如果您添加自己的解析器,您必须注意顺序以及您的解析器添加的位置。

WebMvcAutoConfiguration 将以下 ViewResolver bean 添加到您的上下文中:

  • 一个名为 ‘defaultViewResolver’ 的 InternalResourceViewResolver。 这个解析器定位可以使用 DefaultServlet 渲染的物理资源(包括静态资源和 JSP 页面,如果您使用这些)。 它将前缀和后缀应用于视图名称,然后在 servlet 上下文中查找具有该路径的物理资源(默认值都为空,但可以通过 spring.mvc.view.prefixspring.mvc.view.suffix 从外部配置访问)。 您可以通过提供相同类型的 bean 来覆盖它。

  • 一个名为 ‘beanNameViewResolver’ 的 BeanNameViewResolver。 这是视图解析器链中的一个有用成员,它会选择与正在解析的 View 同名的任何 bean。 不应该需要覆盖或替换它。

  • 一个名为 ‘viewResolver’ 的 ContentNegotiatingViewResolver 仅在实际上存在类型为 View 的 bean 时添加。 这是一个复合解析器,委托给所有其他解析器,并尝试找到与客户端发送的 ‘Accept’ HTTP 头匹配的解析器。 有一个关于 ContentNegotiatingViewResolver 的有用的 博客,您可能想研究它以了解更多信息,您也可以查看源代码以获取详细信息。 您可以通过定义一个名为 ‘viewResolver’ 的 bean 来关闭自动配置的 ContentNegotiatingViewResolver

  • 如果您使用 Thymeleaf,您还有一个名为 ‘thymeleafViewResolver’ 的 ThymeleafViewResolver。 它通过在视图名称周围添加前缀和后缀来查找资源。 前缀是 spring.thymeleaf.prefix,后缀是 spring.thymeleaf.suffix。 前缀和后缀的值默认为 ‘classpath:/templates/’ 和 ‘.html’。 您可以通过提供相同名称的 bean 来覆盖 ThymeleafViewResolver

  • 如果您使用 FreeMarker,您还有一个名为 ‘freeMarkerViewResolver’ 的 FreeMarkerViewResolver。 它通过在视图名称周围添加前缀和后缀,在加载器路径(外部化为 spring.freemarker.templateLoaderPath,默认值为 ‘classpath:/templates/’)中查找资源。 前缀外部化为 spring.freemarker.prefix,后缀外部化为 spring.freemarker.suffix。 前缀和后缀的默认值分别为空和 ‘.ftlh’。 您可以通过提供相同名称的 bean 来覆盖 FreeMarkerViewResolver。 FreeMarker 变量可以通过定义类型为 FreeMarkerVariablesCustomizer 的 bean 来自定义。

  • 如果您使用 Groovy 模板(实际上,如果 groovy-templates 在您的类路径上),您还有一个名为 ‘groovyMarkupViewResolver’ 的 GroovyMarkupViewResolver。 它通过在视图名称周围添加前缀和后缀(外部化为 spring.groovy.template.prefixspring.groovy.template.suffix)在加载器路径中查找资源。 前缀和后缀的默认值分别为 ‘classpath:/templates/’ 和 ‘.tpl’。 您可以通过提供相同名称的 bean 来覆盖 GroovyMarkupViewResolver

  • 如果您使用 Mustache,您还有一个名为 ‘mustacheViewResolver’ 的 MustacheViewResolver。 它通过在视图名称周围添加前缀和后缀来查找资源。 前缀是 spring.mustache.prefix,后缀是 spring.mustache.suffix。 前缀和后缀的值默认为 ‘classpath:/templates/’ 和 ‘.mustache’。 您可以通过提供相同名称的 bean 来覆盖 MustacheViewResolver

有关更多详细信息,请参阅以下部分:

自定义 ‘whitelabel’ 错误页面

Spring Boot 安装了一个 ‘whitelabel’ 错误页面,如果您遇到服务器错误,您会在浏览器客户端中看到它(使用 JSON 和其他媒体类型的机器客户端应该看到带有正确错误代码的合理响应)。

注意:设置 server.error.whitelabel.enabled=false 可以关闭默认错误页面。 这样做会恢复您使用的 servlet 容器的默认值。 请注意,Spring Boot 仍然尝试解析错误视图,因此您可能应该添加自己的错误页面,而不是完全禁用它。

使用您自己的错误页面覆盖错误页面取决于您使用的模板技术。 例如,如果您使用 Thymeleaf,您可以添加一个 error.html 模板。 如果您使用 FreeMarker,您可以添加一个 error.ftlh 模板。 通常,您需要一个解析为 error 名称的 View 或一个处理 /error 路径的 @Controller。 除非您替换了一些默认配置,否则您应该在 ApplicationContext 中找到 BeanNameViewResolver,因此一个名为 error@Bean 将是一种方法。 有关更多选项,请参阅 ErrorMvcAutoConfiguration

另请参阅 错误处理 部分,了解如何在 servlet 容器中注册处理程序。