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 可用,则可以在满足以下额外要求的情况下使用 XML 呈现:MyThing 必须使用 @XmlRootElement 进行注解,如下例所示:

  • 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 中)中进行了描述,这些枚举映射到环境中的属性。

枚举 属性

com.fasterxml.jackson.databind.cfg.EnumFeature

spring.jackson.datatype.enum.<feature_name>

true, false

com.fasterxml.jackson.databind.cfg.JsonNodeFeature

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

true, false

com.fasterxml.jackson.databind.DeserializationFeature

spring.jackson.deserialization.<feature_name>

true, false

com.fasterxml.jackson.core.JsonGenerator.Feature

spring.jackson.generator.<feature_name>

true, false

com.fasterxml.jackson.databind.MapperFeature

spring.jackson.mapper.<feature_name>

true, false

com.fasterxml.jackson.core.JsonParser.Feature

spring.jackson.parser.<feature_name>

true, false

com.fasterxml.jackson.databind.SerializationFeature

spring.jackson.serialization.<feature_name>

true, false

com.fasterxml.jackson.annotation.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 自定义之前和之后应用其他自定义。

任何类型为 com.fasterxml.jackson.databind.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),它将替换默认值。类型为 HttpMessageConverters 的便利 bean 已提供,并且如果您使用默认的 MVC 配置,则始终可用。它有一些有用的方法可以访问默认和用户增强的消息转换器(例如,如果您想将它们手动注入到自定义 RestTemplate 中,这将很有用)。

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

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

处理多部分文件上传

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

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

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

建议使用容器内置的多部分上传支持,而不是引入其他依赖项(例如 Apache Commons File Upload)。

关闭 Spring MVC DispatcherServlet

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

  • 属性

  • 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 实现。请注意,ViewResolvers 主要用于 UI 应用程序,而不是 REST 风格的服务(View 不用于呈现 @ResponseBody)。有许多 ViewResolver 的实现可供选择,Spring 本身并不偏好您应该使用哪一个。另一方面,Spring Boot 会为您安装一个或两个,具体取决于它在类路径和应用程序上下文中找到的内容。DispatcherServlet 使用它在应用程序上下文中找到的所有解析器,依次尝试每个解析器,直到获得结果。如果您添加了自己的解析器,则必须注意顺序以及添加解析器的位置。

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

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

  • 一个名为“beanNameViewResolver”的 BeanNameViewResolver。这是视图解析器链的有用成员,它会获取与要解析的 View 名称相同的任何 bean。通常不需要覆盖或替换它。

  • 仅当实际存在类型为 View 的 bean 时,才会添加一个名为“viewResolver”的 ContentNegotiatingViewResolver。这是一个复合解析器,它委托给所有其他解析器,并尝试找到与客户端发送的“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

  • 如果您使用 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

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

自定义“白标”错误页面

Spring Boot 安装了一个“白标”错误页面,如果您遇到服务器错误,您将在浏览器客户端中看到该页面(使用 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 容器中注册处理程序的详细信息。