领先一步
VMware 提供培训和认证,以加速您的进步。
了解更多这篇文章重点介绍了 Spring 3.1 M2 中 Spring MVC 的新功能。以下是主题
简要提醒一下,此处讨论的功能在 Greenhouse 项目中处于活动状态。
正如 Chris 上周五在其 博客文章 中指出的那样,XML 命名空间极大地减少了配置,但也降低了透明度,有时还会降低灵活性。这对 MVC 命名空间也适用,它支持许多自定义,但并非所有可用的功能都支持。这意味着您可以使用它,或者不使用它。我们相信基于代码的配置可以解决这个问题,并提供从简单到高级的路径。
让我们从这个简单、熟悉的代码片段开始
<mvc:annotation-driven />
虽然不需要用于使用带注解的控制器,但 <mvc:annotation-driven>
会执行许多有用的操作——它检测 JSR-303(Bean 验证)实现的存在并使用它配置数据绑定,如果 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 支持类,它们为您提供了更多功能,并为我们提供了更好的构建基础。
在注解之前,Controller 是处理端点。使用注解后,单个控制器方法成为端点,并具有其自己的请求映射。遵循此逻辑,HandlerMapping
不应局限于选择控制器,而应选择控制器方法。因此,添加 HandlerMethod
抽象和用于可以选择 HandlerMethod
的处理程序映射的 AbstractHandlerMethodMapping
很有意义。
这些是围绕 HandlerMethod
抽象构建的新 @MVC 支持类
RequestMappingHandlerMapping
RequestMappingHandlerAdapter
ExceptionHandlerExceptionResolver
因此,我们现在在处理程序映射中有了一个选择点,处理程序适配器确切地知道选择了哪个处理程序方法,其他组件也一样。例如,许多人要求在处理程序方法的调用周围进行拦截,现在已经弥补了这一差距。另一个不太明显的结果是能够将相同的 URL 映射到不同的控制器,只要映射在其他方面有所不同(例如 HTTP 方法)。
除了请求映射之外,控制器方法的执行还需要解析方法参数(@RequestParameter
、@ModelAttribute
等)和处理返回值(@ResponseBody
、@ModelAttribute
等)。新的 @MVC 支持类公开了可插拔机制,其中可以插入 HandlerMethodArgumentResolver
和 HandlerMethodReturnValueHandler
的实现来解析每个方法参数和处理每个返回值。您可以完全控制它——您可以设计自己的参数类型和返回值类型,或者自定义内置类型的处理。更多详细信息将在后续文章中介绍。
要尝试新的 @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. 重定向字符串支持使用模型中的变量(包括声明的 @PathVariables
)扩展的 URI 模板。例如,以下代码
@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。