本章内容:
映射请求到Spring控制器
透明地绑定表单参数
校验表单提交
作为企业级Java开发者,你可能开发过一些基于Web的应用程序。对于很多Java开发人员来说,基于Web的应用程序是他们主要的关注点。如果你有这方面经验的话,你会意识到这种系统所面临的挑战。具体来讲,状态管理、工作流以及验证都是需要解决的重要特性。HTTP协议的无状态性决定了这些问题都不那么容易解决。
Spring的Web框架就是为了帮你解决这些关注点而设计的。SpringMVC基于模型-视图-控制器(Model-View-Controller,MVC)模式实现,它能够帮你构建像Spring框架那样灵活和松耦合的Web应用程序。
在本章中,我们将会介绍Spring MVC Web框架,并使用新的Spring MVC注解来构建处理各种Web请求、参数和表单输入的控制器。在深入介绍Spring MVC之前,让我们先总体上介绍一下Spring MVC,并建立起Spring MVC运行的基本配置。
5.1 Spring MVC起步
Spring MVC框架主要包括请求调度Servlet、处理器映射(handler mapping)、控制器以及视图解析器(view resolver)这些组件。
跟踪Spring MVC的请求
Web请求从离开浏览器开始到获取响应返回,它会经历好多站,每站都会留下一些信息同时带上其他信息。
一路上请求会将信息带到很多站点,并产生期望的结果
请求带着URL以及其他信息离开浏览器后,第一站是Spring的DispatcherServlet(前端控制器)。它的任务将请求发送给Spring MVC控制器。由于可能有多个控制器,DispatcherServlet会查询一个或多个处理器映射来确定请求的下一站,处理器映射会根据URL信息来决策。选择合适的控制器后,DispatcherServlet将请求发送给选中的控制器。到了控制器,它会处理请求带来的信息。
控制器处理完成后,会将模型数据打包,并且标示出用于渲染输出的视图名。它接下来将请求连同模型和视图名发送回DispatcherServlet,目的是解耦。传递给DispatcherServlet的视图名并不直接标示某个特定的JSP。它仅仅传递了一个逻辑名称,用来查找产生结果的真正视图。DispatcherServlet将会使用视图解析器来将逻辑视图名匹配为一个特定的视图实现(可能是JSP)。
DispatcherServlet的最后一站是视图的实现(可能是JSP),在这里它交付模型数据。视图将使用模型数据渲染输出,通过响应对象传递给客户端。
搭建Spring MVC
首先配置DispatcherServlet,它是Spring MVC的核心。如果按传统方式,DispatcherServlet会配置在web.xml中。另一种方式可以将DispatcherServlet配置在Servlet容器中。
package spittr.config;import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;public class SpittrWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Overrideprotected Class>[] getRootConfigClasses() {
return new Class>[] { RootConfig.class }; }@Override // 指定配置类protected Class>[] getServletConfigClasses() {
return new Class>[] { WebConfig.class }; }@Override // 将DispatcherServlet映射到“/”protected String[] getServletMappings() {
return new String[]{ "/"}; }}
我们需要知道的是AbstractAnnotationConfigDispatcherServletInitializer的任意类都会自动地配置DispatcherServlet和Spring应用上下文,Spring的应用上下文会位于应用程序的Servlet上下文中。
AbstractAnnotationConfigDispatcherServletInitializer剖析
在Servlet3.0环境中,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类,如果能发现的话,就会用它来配置Servlet容器。
Spring提供了这个接口的实现,名为SpringServletContainerInitializer,这个类反过来又会查找实现WebApplicationInitializer的类并将配置的任务交给它们来完成。Spring3.2引入了一个便利的WebApplicationInitializer基础实现,也就是AbstractAnnotationConfigDispatcherServletInitializer。因为我们的SpittrWebAppInitializer扩展了AbstractAnnotationConfigDispatcherServletInitializer(同时也就实现了WebApplicationInitializer),因此当部署到Servlet3.0容器中的时候,容器会自动发现它,并用它来配置Servlet上下文。
在本例中,SpittrWebAppInitializer重新了三个方法。
第一个方法是getServletMappings(),它会将一个或多个路径映射到DispatcherServlet上。本例中,它映射的是“/”,这表示它会是应用的默认Servlet。它会处理进入应用的所有请求。
为了理解其他的两个方法,首先要理解DispatcherServlet和一个Servlet监听器(也就是ContextLoaderListener)的关系。
当DispatcherServlet启动时,它会创建Spring应用上下文,并加载配置文件或配置类中所声明的bean。在代码中的getServletConfigClasses()方法中,我们要求DispatcherServlet加载应用上下文时,使用定义在WebConfig配置类(使用Java配置)中的bean。
但是在Spring Web应用中,通常还会有另外一个应用上下文。另外的这个应用上下文是由ContextLoaderListener创建的。
我们希望DispatcherServlet加载包含Web组件的bean,如控制器、视图解析器以及处理器映射,而ContextLoaderListener要加载应用中的其他bean。这些bean通常是驱动应用后端的中间层和数据层组件。
实际上,AbstractAnnotationConfigDispatcherServletInitializer会同时创建
DispatcherServlet和ContextLoaderListener。getServletConfigClasses()方法返回的带有@Configuration注解的类将会用来定义DispatcherServlet应用上下文中的bean。getRootConfigClasses()方法返回的带有@Configuration注解的类将会用来配置ContextLoaderListener创建的应用上下文中bean。
在本例中,根配置定义在RootConfig中,DispatcherServlet的配置声明在WebConfig中。
如果按照这种方式配置DispatcherServlet,它只能部署到支持Servlet 3.0的服务器中才能正常工作,如Tomcat 7或更高版本。
启动Spring MVC
启动Spring MVC组件的方法也不仅一种。除了传统的XML配置,还可选择基于Java进行配置。
package spittr.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.ViewResolver;import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;import org.springframework.web.servlet.config.annotation.EnableWebMvc;import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;import org.springframework.web.servlet.view.InternalResourceViewResolver;@Configuration@ComponentScan("spittr.web") // 启动组件扫描@EnableWebMvc // 启动spring mvcpublic class WebConfig extends WebMvcConfigurerAdapter {
@Beanpublic ViewResolver viewResolver(){ // 配置jsp视图解析器 InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/views/"); resolver.setSuffix(".jsp"); resolver.setExposeContextBeansAsAttributes(true);return resolver; }// 配置静态资源的处理@Overridepublic void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable(); }}
程序中第一件需要注意的事情就是WebConfig添加了@ComponentScan注解,因此将会扫描spitter.web包来查找组件。
接下来,添加了viewResolver bean,表示配置视图解析器。如果没有配置,则Spring默认会使用BeanNameViewResolver,这个视图解析器会查找ID与视图名称匹配的bean,并且查找的bean要实现View接口。而InternalResourceView