71. Spring MVC

71.1 编写JSON REST服务

只要添加的有Jackson2依赖,Spring Boot应用中的任何@RestController默认都会渲染为JSON响应,例如:

@RestController
public class MyController {

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

}

只要MyThing能够通过Jackson2序列化(比如,一个标准的POJO或Groovy对象),默认localhost:8080/thing将响应一个JSON数据。有时在浏览器中你可能看到XML响应,因为浏览器倾向于发送XML accept headers。

71.2 编写XML REST服务

如果classpath下存在Jackson XML扩展(jackson-dataformat-xml),它会被用来渲染XML响应,示例和JSON的非常相似。想要使用它,只需为你的项目添加以下依赖:

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

你可能还需要添加Woodstox的依赖,它比JDK提供的默认StAX实现快很多,并且支持良好的格式化输出,提高了namespace处理能力:

<dependency>
    <groupId>org.codehaus.woodstox</groupId>
    <artifactId>woodstox-core-asl</artifactId>
</dependency>

如果Jackson的XML扩展不可用,Spring Boot将使用JAXB(JDK默认提供),不过MyThing需要注解@XmlRootElement

@XmlRootElement
public class MyThing {
    private String name;
    // .. getters and setters
}

想要服务器渲染XML而不是JSON,你可能需要发送一个Accept: text/xml头部(或使用浏览器)。

71.3 自定义Jackson ObjectMapper

在一个HTTP交互中,Spring MVC(客户端和服务端)使用HttpMessageConverters协商内容转换。如果classpath下存在Jackson,你就获取到Jackson2ObjectMapperBuilder提供的默认转换器,这是Spring Boot为你自动配置的实例。

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

  • MapperFeature.DEFAULT_VIEW_INCLUSION,默认是禁用的
  • DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,默认是禁用的

Spring Boot也有一些用于简化自定义该行为的特性。

你可以使用当前的environment配置ObjectMapperXmlMapper实例。Jackson提供一个扩展套件,可以用来关闭或开启一些特性,你可以用它们配置Jackson以处理不同方面。这些特性在Jackson中是使用6个枚举进行描述的,并被映射到environment的属性上:

Jackson枚举 Environment属性
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.serialization-inclusion=always|non_null|non_absent|non_default|non_empty

例如,设置spring.jackson.serialization.indent_output=true可以美化打印输出(pretty print)。注意,由于松散绑定的使用,indent_output不必匹配对应的枚举常量INDENT_OUTPUT

基于environment的配置会应用到自动配置的Jackson2ObjectMapperBuilder bean,然后应用到通过该builder创建的mappers,包括自动配置的ObjectMapper bean。

ApplicationContext中的Jackson2ObjectMapperBuilder可以通过Jackson2ObjectMapperBuilderCustomizer bean自定义。这些customizer beans可以排序,Spring Boot自己的customizer序号为0,其他自定义可以应用到Spring Boot自定义之前或之后。

所有类型为com.fasterxml.jackson.databind.Module的beans都会自动注册到自动配置的Jackson2ObjectMapperBuilder,并应用到它创建的任何ObjectMapper实例。这提供了一种全局机制,用于在为应用添加新特性时贡献自定义模块。

如果想完全替换默认的ObjectMapper,你既可以定义该类型的@Bean并注解@Primary,也可以定义Jackson2ObjectMapperBuilder @Bean,通过builder构建。注意不管哪种方式都会禁用所有的自动配置ObjectMapper

如果你提供MappingJackson2HttpMessageConverter类型的@Bean,它们将替换MVC配置中的默认值。Spring Boot也提供了一个HttpMessageConverters类型的便利bean(如果你使用MVC默认配置,那它就总是可用的),它提供了一些有用的方法来获取默认和用户增强的消息转换器(message converters)。具体详情可参考Section 71.4, “Customize the @ResponseBody rendering”WebMvcAutoConfiguration源码。

71.4 自定义@ResponseBody渲染

Spring使用HttpMessageConverters渲染@ResponseBody(或来自@RestController的响应),你可以通过在Spring Boot上下文中添加该类型的beans来贡献其他的转换器。如果你添加的bean类型默认已经包含了(像用于JSON转换的MappingJackson2HttpMessageConverter),那它将替换默认的。Spring Boot提供一个方便的HttpMessageConverters类型的bean,它有一些有用的方法可以访问默认的和用户增强的message转换器(比如你想要手动将它们注入到一个自定义的RestTemplate时就很有用)。

在通常的MVC用例中,任何你提供的WebMvcConfigurerAdapter beans通过覆盖configureMessageConverters方法也能贡献转换器,但不同于通常的MVC,你可以只提供你需要的转换器(因为Spring Boot使用相同的机制来贡献它默认的转换器)。最终,如果你通过提供自己的@EnableWebMvc注解覆盖Spring Boot默认的MVC配置,那你就可以完全控制,并使用来自WebMvcConfigurationSupportgetMessageConverters手动做任何事。

更多详情可参考WebMvcAutoConfiguration源码。

71.5 处理Multipart文件上传

Spring Boot采用Servlet 3 javax.servlet.http.Part API来支持文件上传。默认情况下,Spring Boot配置Spring MVC在单个请求中只处理每个文件最大1Mb,最多10Mb的文件数据。你可以覆盖那些值,也可以设置临时文件存储的位置(比如,存储到/tmp文件夹下)及传递数据刷新到磁盘的阀值(通过使用MultipartProperties类暴露的属性)。如果你需要设置文件不受限制,可以设置spring.http.multipart.max-file-size属性值为-1

当你想要接收multipart编码文件数据作为Spring MVC控制器(controller)处理方法中被@RequestParam注解的MultipartFile类型的参数时,multipart支持就非常有用了。

更多详情可参考MultipartAutoConfiguration源码。

71.6 关闭Spring MVC DispatcherServlet

Spring Boot想要服务来自应用程序root /下的所有内容。如果你想将自己的servlet映射到该目录下也是可以的,但当然你可能失去一些Spring Boot MVC特性。为了添加你自己的servlet,并将它映射到root资源,你只需声明一个Servlet类型的@Bean,并给它特定的bean名称dispatcherServlet(如果只想关闭但不替换它,你可以使用该名称创建不同类型的bean)。

71.7 关闭默认的MVC配置

完全控制MVC配置的最简单方式是提供你自己的被@EnableWebMvc注解的@Configuration,这样所有的MVC配置都逃不出你的掌心。

71.8 自定义ViewResolvers

ViewResolver是Spring MVC的核心组件,它负责转换@Controller中的视图名称到实际的View实现。注意ViewResolvers主要用在UI应用中,而不是REST风格的服务(View不是用来渲染@ResponseBody的)。Spring有很多你可以选择的ViewResolver实现,并且Spring自己对如何选择相应实现也没发表意见。另一方面,Spring Boot会根据classpath上的依赖和应用上下文为你安装一或两个ViewResolver实现。DispatcherServlet使用所有在应用上下文中找到的解析器(resolvers),并依次尝试每一个直到它获取到结果,所以如果你正在添加自己的解析器,那就要小心顺序和你的解析器添加的位置。

WebMvcAutoConfiguration将会为你的上下文添加以下ViewResolvers

  • bean id为defaultViewResolverInternalResourceViewResolver,它会定位可以使用DefaultServlet渲染的物理资源(比如静态资源和JSP页面)。它在视图名上应用了一个前缀和后缀(默认都为空,但你可以通过spring.view.prefixspring.view.suffix设置),然后查找在servlet上下文中具有该路径的物理资源,可以通过提供相同类型的bean覆盖它。
  • id为beanNameViewResolverBeanNameViewResolver,它是视图解析器链的一个非常有用的成员,可以在View解析时收集任何具有相同名称的beans,没必要覆盖或替换它。
  • id为viewResolverContentNegotiatingViewResolver,它只会在实际View类型的beans出现时添加。这是一个'master'解析器,它的职责会代理给其他解析器,它会尝试找到客户端发送的一个匹配'Accept'的HTTP头部。这有一篇关于ContentNegotiatingViewResolver的博客,你也可以也查看下源码。通过定义一个名叫'viewResolver'的bean,你可以关闭自动配置的ContentNegotiatingViewResolver
  • 如果使用Thymeleaf,你将有一个id为thymeleafViewResolverThymeleafViewResolver,它会通过加前缀和后缀的视图名来查找资源(外部配置为spring.thymeleaf.prefixspring.thymeleaf.suffix,对应的默认为'classpath:/templates/'和'.html')。你可以通过提供相同名称的bean来覆盖它。
  • 如果使用FreeMarker,你将有一个id为freeMarkerViewResolverFreeMarkerViewResolver,它会使用加前缀和后缀(外部配置为spring.freemarker.prefixspring.freemarker.suffix,对应的默认值为空和'.ftl')的视图名从加载路径(外部配置为spring.freemarker.templateLoaderPath,默认为'classpath:/templates/')下查找资源。你可以通过提供相同名称的bean来覆盖它。
  • 如果使用Groovy模板(实际上只要你把groovy-templates添加到classpath下),你将有一个id为groovyTemplateViewResolverGroovy TemplateViewResolver,它会使用加前缀和后缀(外部属性为spring.groovy.template.prefixspring.groovy.template.suffix,对应的默认值为'classpath:/templates/'和'.tpl')的视图名从加载路径下查找资源。你可以通过提供相同名称的bean来覆盖它。
  • 如果使用Velocity,你将有一个id为velocityViewResolverVelocityViewResolver,它会使用加前缀和后缀(外部属性为spring.velocity.prefixspring.velocity.suffix,对应的默认值为空和'.vm')的视图名从加载路径(外部属性为spring.velocity.resourceLoaderPath,默认为'classpath:/templates/')下查找资源。你可以通过提供相同名称的bean来覆盖它。

更多详情可查看源码:  WebMvcAutoConfigurationThymeleafAutoConfigurationFreeMarkerAutoConfigurationGroovyTemplateAutoConfigurationVelocityAutoConfiguration

71.9 Velocity

默认情况下,Spring Boot会配置一个VelocityViewResolver,如果需要的是VelocityLayoutViewResolver,你可以自己创建一个名为velocityViewResolver的bean。你也可以将VelocityProperties实例注入到自定义视图解析器以获取基本的默认设置。

以下示例使用VelocityLayoutViewResolver替换自动配置的velocity视图解析器,并自定义layoutUrl及应用所有自动配置的属性:

@Bean(name = "velocityViewResolver")
public VelocityLayoutViewResolver velocityViewResolver(VelocityProperties properties) {
    VelocityLayoutViewResolver resolver = new VelocityLayoutViewResolver();
    properties.applyToViewResolver(resolver);
    resolver.setLayoutUrl("layout/default.vm");
    return resolver;
}

71.10 使用Thymeleaf 3

默认情况下,spring-boot-starter-thymeleaf使用的是Thymeleaf 2.1,你可以通过覆盖thymeleaf.versionthymeleaf-layout-dialect.version属性使用Thymeleaf 3,例如:

<properties>
    <thymeleaf.version>3.0.0.RELEASE</thymeleaf.version>
    <thymeleaf-layout-dialect.version>2.0.0</thymeleaf-layout-dialect.version>
</dependency>

为了避免关于HTML 5模板模式过期,将使用HTML模板模式的警告提醒,你需要显式配置spring.thymeleaf.modeHTML,例如:

spring.thymeleaf.mode: HTML

具体操作可查看Thymeleaf 3示例

如果正在使用其他自动配置的Thymeleaf附加组件(Spring Security,Data Attribute或Java 8 Time),你需要使用兼容Thymeleaf 3.0的版本覆盖它们现在的版本。