Spring 3.1 M2:Spring MVC 增强功能

工程 | Rossen Stoyanchev | 2011年6月13日 | ...

本文重点介绍 Spring 3.1 M2 中 Spring MVC 的新特性。以下是相关主题:

  • MVC 命名空间的基于代码的等效项。
  • 可自定义的 @MVC 处理。
  • 编程模型改进。

简要提醒一下,本文讨论的功能在 Greenhouse 项目中已得到应用。

Spring MVC 的基于代码的配置

正如 Chris 在上周五的博客文章中所指出的,XML 命名空间极大地简化了配置,但也降低了透明度,有时也牺牲了灵活性。这同样适用于 MVC 命名空间,它支持许多自定义,但并非所有可用的自定义都支持。这意味着您要么可以使用它,要么就得放弃它。我们认为基于代码的配置可以解决这个问题,并提供从简单到高级的路径。

让我们从这个简单而熟悉的片段开始


<mvc:annotation-driven />

虽然使用注解式控制器不是必需的,但 <mvc:annotation-driven> 做了许多有用的事情——它会检测 JSR-303 (Bean Validation) 实现的存在并与之集成数据绑定,如果 Jackson JSON 库可用,它会添加一个 JSON 消息转换器,以及其他一些可以节省大量配置的工作。

现在,让我们将其与基于代码的配置进行匹配


@Configuration
@EnableWebMvc
public class WebConfig {
}

这里的@EnableWebMvc 导入了一个 @Configuration 类,它提供了与 <mvc:annotation-driven> 相同的功能。就是这么简单。

下一步是使用 <mvc:annotation-driven> 中的属性,例如提供一个 FormattingConversionService,或者添加一个子元素,例如配置消息转换器,或者使用其他 MVC 命名空间元素,如 <mvc:interceptors><mvc:resources> 等。

让我们看看如何在代码中完成这一切


@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // register converters and formatters...
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        // add message converters...
    }

    @Override
    public void configureInterceptors(InterceptorConfigurer configurer) {
        configurer.addInterceptor(new AccountExposingHandlerInterceptor());
    }

    @Override
    public void configureResourceHandling(ResourceConfigurer configurer) {
        configurer.addPathMapping("/resources/**").addResourceLocation("/resources/");
    }

    // more @Override methods ... 

}

请注意,这次我们还对 WebMvcConfigurerAdapter 进行了子类化。这是一个方便的基类,其中只包含 WebMvcConfigurer 接口的方法实现,该接口定义了用于自定义 Spring MVC 配置的回调。实现了该接口的 @Configuration 类会被检测到,并有机会应用自定义设置。基本上就是这样。

上述方法以一种更透明的方式提供了与 MVC 命名空间相匹配的功能。您可以使用熟悉的 Java IDE 快捷键来探索 WebMvcConfigurer 接口以及已导入的 @Configuration 类的 @Bean 方法。

更高级的自定义呢?嗯,这是 MVC 命名空间所能达到的极限。对于基于代码的配置,在下一个(RC1)版本中,您可以按原样使用上面的示例,删除已导入的配置(即删除 @EnableWebMvc),然后切换到一个包含可以覆盖的 @Bean 方法的基类。这意味着您可以使用 Spring MVC 的基于代码的配置,并且知道任何级别的自定义都是可能的——无论是通过简单的回调机制,还是直接从提供实际配置的类进行扩展。

这里是 Greenhouse 中 Web 配置 的概览。

可自定义的 @MVC 处理

@MVC 编程模型在启用灵活的控制器方法签名方面取得了巨大成功。然而,许多用户要求对底层的 @MVC 支持类进行更多自定义。作为回应,我们推出了一套新的 @MVC 支持类,它们为您提供了更多功能,也为我们打下了更好的基础。

在注解之前,控制器是处理的端点。有了注解,单个控制器方法就成为了端点,并带有自己的请求映射。按照这个逻辑,HandlerMapping 不应该仅限于选择一个控制器,而应该选择一个控制器方法。因此,我们添加了一个 HandlerMethod 抽象以及一个 AbstractHandlerMethodMapping,用于可以选择 HandlerMethod 的处理器映射,这是有道理的。

这些是围绕 HandlerMethod 抽象构建的新 @MVC 支持类:

  • RequestMappingHandlerMapping
  • RequestMappingHandlerAdapter
  • ExceptionHandlerExceptionResolver

因此,我们在处理器映射中现在有了一个单一的选择点。处理器适配器确切地知道选择了哪个处理器方法,其他组件也是如此。例如,许多用户要求在处理器方法的调用周围进行拦截,现在这个缺口已经弥补了。另一个不太明显的后果是,可以将同一个 URL 映射到不同的控制器,只要映射在其他方面有所不同(例如 HTTP 方法)。

除了请求映射之外,控制器方法的执行还需要解析方法参数(@RequestParameter@ModelAttribute 等)并处理返回值(@ResponseBody@ModelAttribute 等)。新的 @MVC 支持类公开了一个可插入的机制,其中 HandlerMethodArgumentResolverHandlerMethodReturnValueHandler 的实现可以被插入,以解析每一个方法参数并处理每一个返回值。您对此拥有完全的控制权——您可以设计自己的参数类型和返回值类型,或者自定义内置参数的处理。更多细节将在后续帖子中介绍。

要尝试新的 @MVC 支持类,只需升级到 Spring 3.1 M2。MVC 命名空间和 @EnableWebMvc 都会配置它们。或者,如果您有自己的配置,只需替换这些:

  • DefaultAnnotationHandlerMapping -> RequestMappingHandlerMapping
  • AnnotationMethodHandlerAdapter -> RequestMappingHandlerAdapter
  • AnnotationMethodExceptionResolver -> ExceptionHandlerExceptionResolver

关于兼容性的一点说明:现有的支持类将继续可用。但是,我们建议您以后进行切换。例如,下一节中的所有编程模型改进都只能通过这种方式获得。新类在大多数情况下应该是即插即用的替代品,但有两个值得注意的区别。一,您不能将现有的 AbstractUrlHandlerMapping 类型(例如 SimpleUrlHandlerMapping)与新的处理器适配器结合使用,因为它需要 HandlerMethod 而不是处理器。二,当两个 @RequestMapping 方法对一个请求的匹配度相同时,您不能依赖方法名称。

编程模型改进

本节列出了新 @MVC 支持类中引入的编程模型改进。

1. 声明的 @PathVariable 参数现在会自动添加到模型中。例如,这个


@RequestMapping("/develop/apps/edit/{slug}")
public String editForm(@PathVariable String slug, Model model) {
	model.addAttribute("slug", slug);
    // ...
}

被替换为


@RequestMapping("/develop/apps/edit/{slug}")
public String editForm(@PathVariable String slug, Model model) {
    // model contains "slug" variable
}

2. 重定向字符串支持 URI 模板,并使用模型中的变量(包括声明的 @PathVariables)进行扩展。例如,这个


@RequestMapping(
    value="/groups/{group}/events/{year}/{month}/{slug}/rooms", 
    method=RequestMethod.POST) 
public String createRoom(
    @PathVariable String group, @PathVariable Integer year, 
    @PathVariable Integer month, @PathVariable String slug) {
    // ...	
    return "redirect:/groups/" + group + "/events/" + year + "/" + month + "/" + slug;
}

被替换为


@RequestMapping(
    value="/groups/{group}/events/{year}/{month}/{slug}/rooms", 
    method=RequestMethod.POST) 
public String createRoom(
    @PathVariable String group, @PathVariable Integer year, 
    @PathVariable Integer month, @PathVariable String slug) {
    // ...	
    return "redirect:/groups/{group}/events/{year}/{month}/{slug}";
}

3. URI 模板变量在数据绑定中得到支持。例如,这个


@RequestMapping("/people/{firstName}/{lastName}/SSN")
public String find(Person person, 
                   @PathVariable String firstName, 
                   @PathVariable String lastName) {
    person.setFirstName(firstName);
    person.setLastName(lastName);
    // ...
}

被替换为


@RequestMapping("/people/{firstName}/{lastName}/SSN")
public String search(Person person) {
    // person.getFirstName() and person.getLastName() are populated
    // ...
}

4. 可消耗和可生成的媒体类型可以通过 @RequestMapping 指定。例如,这个


@RequestMapping(value="/pets", headers="Content-Type=application/json")
public void addPet(@RequestBody Pet pet, Model model) {
    // ...
}

被替换为


@RequestMapping(value="/pets", consumes="application/json")
public void addPet(@RequestBody Pet pet, Model model) {
    // ...
}

除了更简短之外,如果 URL 匹配但输入媒体类型不匹配,上面的代码将返回 NOT_ACCEPTABLE (406)。

5. 对于可生成的媒体类型,这个


@Controller
@RequestMapping(value = "/pets/{petId}", headers="Accept=application/json")
@ResponseBody
public Pet getPet(@PathVariable String petId, Model model) {    
    // ...
}

被替换为


@Controller
@RequestMapping(value = "/pets/{petId}", produces="application/json")
@ResponseBody
public Pet getPet(@PathVariable String petId, Model model) {    
    // ...
}

如果 URL 匹配但可接受的媒体类型不匹配,上面的代码将返回 NOT_SUPPORTED_MEDIA_TYPE (415)。

总结

这个里程碑版本有很多新内容。我鼓励大家在 RC1 和 GA 版本发布之前尝试这些更改并提供反馈。

我还想提请您注意另一项正在进行的努力,即为 Spring MVC 应用程序提供集成测试支持。有关服务器端测试支持,请参阅 Github 上的 spring-test-mvc 项目。有关客户端支持,请查看 Spring Social 项目或跟踪以下 JIRA 票据 SPR-7951

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看所有