Spring MVC
Spring Boot 提供了许多包含 Spring MVC 的 starter。需要注意的是,某些 starter 包含对 Spring MVC 的依赖,而不是直接包含它。本节将回答有关 Spring MVC 和 Spring Boot 的常见问题。
编写 JSON REST 服务
在 Spring Boot 应用程序中,只要类路径上存在 Jackson2,任何使用 @RestController 注解的 Spring 控制器默认都会渲染 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](http://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 还提供了一些功能来简化这种行为的自定义。
你可以通过使用环境来配置 ObjectMapper 和 XmlMapper 实例。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 |
com.fasterxml.jackson.core.JsonGenerator.Feature | spring.jackson.generator.<feature_name> | true , false |
MapperFeature | spring.jackson.mapper.<feature_name> | true , false |
com.fasterxml.jackson.core.JsonParser.Feature | spring.jackson.parser.<feature_name> | true , false |
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 的自定义之前和之后应用额外的自定义。
任何类型为 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),它将替换默认值。Spring 提供了一个类型为 HttpMessageConverters 的便捷 Bean,如果你使用默认的 MVC 配置,它总是可用的。它提供了一些有用的方法来访问默认的和用户增强的消息转换器(例如,如果你想要手动将它们注入到一个自定义的 RestTemplate 中,这会非常有用)。
在正常的 MVC 使用中,你提供的任何 WebMvcConfigurer Bean 也可以通过重写 configureMessageConverters
方法来贡献转换器。然而,与正常的 MVC 不同,你只能提供你需要的额外转换器(因为 Spring Boot 使用相同的机制来贡献其默认值)。最后,如果你通过提供自己的 @EnableWebMvc 配置来退出默认的 Spring Boot MVC 配置,你可以完全控制并手动完成所有操作,方法是使用 WebMvcConfigurationSupport 中的 getMessageConverters
。
详情请参阅 WebMvcAutoConfiguration 源码。
处理多部分文件上传
Spring Boot 支持使用 Servlet 5 的 Part API 来实现文件上传功能。默认情况下,Spring Boot 配置 Spring MVC 时,单个文件的最大大小为 1MB,单个请求中文件数据的总大小最大为 10MB。你可以通过使用 MultipartProperties 类中提供的属性来覆盖这些值,设置中间数据的存储位置(例如,存储到 /tmp
目录),以及指定数据刷新到磁盘的阈值。例如,如果你希望文件大小不受限制,可以将 spring.servlet.multipart.max-file-size
属性设置为 -1
。
当你想在 Spring MVC 控制器处理方法中接收以 multipart
编码的文件数据作为类型为 MultipartFile
的 @RequestParam
注解参数时,多部分支持非常有用。
请参阅 MultipartAutoConfiguration 源码以获取更多详细信息。
建议使用容器内置的多部分上传支持,而不是引入额外的依赖项,例如 Apache Commons File Upload。
关闭 Spring MVC 的 DispatcherServlet
默认情况下,所有内容都是从应用程序的根路径(/
)提供的。如果您希望映射到不同的路径,可以按如下方式进行配置:
- Properties
- YAML
spring.mvc.servlet.path=/mypath
spring:
mvc:
servlet:
path: "/mypath"
如果你有额外的 servlets,你可以为每个 servlet 声明一个类型为 @Bean 的 Servlet 或 ServletRegistrationBean,Spring Boot 会透明地将它们注册到容器中。由于 servlets 是以这种方式注册的,它们可以被映射到 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.prefix
和spring.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。可以通过定义 FreeMarkerVariablesCustomizer 类型的 bean 来自定义 FreeMarker 变量。 -
如果你使用 Groovy 模板(实际上,如果
groovy-templates
在你的类路径上),还会有一个名为groovyMarkupViewResolver
的 GroovyMarkupViewResolver。它通过在加载路径中为视图名称添加前缀和后缀(外部化为spring.groovy.template.prefix
和spring.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 安装了一个“白标”错误页面,当你在浏览器客户端遇到服务器错误时,会看到这个页面(如果是机器客户端消费 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 容器中注册处理程序的详细信息。