在整个Spring MVC框架中,DispatcherServlet处于核心位置,它负责协调和组织不同组件完成请求处理并返回响应的工作。具体流程为:
1)客户端发送http请求,web应用服务器接收到这个请求,如果匹配DispatcherServlet的映射路径(在web.xml中配置),web容器将请求转交给DispatcherServlet处理;2)DispatcherServlet根据请求的信息及HandlerMapping的配置找到处理该请求的Controller;3)Controller完成业务逻辑处理后,返回一个ModelAndView给DispatcherServlet;4)DispatcherServlet借由ViewResolver完成ModelAndView中逻辑视图名到真实视图对象View的解析工作;5)DispatcherServlet根据ModelAndView中的数据模型对View对象进行视图渲染,最终客户端得到的响应消息可能是一个普通的html页面,也可能是一个xml或json串,甚至是一张图片或一个PDF文档等不同的媒体形式。Spring提供了DispatcherServlet,这个类不仅负责实现请求转发,还负责启动一个WebApplicationContext容器。 按照Spring一贯的IoC哲学,所有的Controller都是JavaBean,并由IoC容器统一管理。对于View,则采取了更灵活的处理方 式,Spring MVC允许使用不同的View实现,除了JSP外,还可以使用Velocity、Freemaker、XSLT等多种View技术。总的来讲,要使用Spring MVC框架,需要以下步骤。① 在web.xml中配置DispatcherServlet及URL映射。② 编写IoC容器需要的XML配置文件,命名为<servlet-name>-servlet.xml,放到/WEB-INF目录下。例如,如果DispatcherServlet在web.xml中的配置名称为dispatcher,则Spring将寻找dispatcher- servlet.xml配置文件。③ 在XML配置文件中定义URL映射方式和使用哪种View技术。我们仍以SimpleMVC为基础,用Spring MVC框架来实现这个Web应用程序。在Eclipse中创建一个SpringMVC项目,结构如图7-21所示。/web/WEB-INF/lib目录下的jstl.jar和standard.jar是JSP标准标签库,将在JSP视图中用到,/web/WEB-INF/c.tld是标签库的声明文件,稍后我们会在JSP视图文件中用到它们。一、 配置DispatcherServlet首先,在web.xml中配置DispatcherServlet,并将所有以“.html”结尾的URL全部映射到DispatcherServlet上,这样,用户仅从URL地址上无法得知服务器端后台使用了何种技术。<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"><web-app> <!-- spring dispatch servlet --> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</ servlet-class> <load-on-startup>0</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> <!-- 申明taglib,将在JSP中使用 --> <taglib> <taglib-uri>http://java.sun.com/jsp/jstl/core</taglib-uri> <taglib-location>/WEB-INF/c.tld</taglib-location> </taglib></web-app>和普 通的Spring应用程序稍有不同,对于Web应用程序,Spring的ApplicationContext是由DispatcherServlet加 载的,它会在/WEB-INF/下查找一个名称为<servletName> -servlet.xml的XML配置文件来初始化Spring Web应用程序的ApplicationContext。对于上例,我们在web.xml中定义DispatcherServlet的名称为 dispatcher,因此,相应的XML配置文件就必须是/WEB-INF/dispatcher-servlet.xml。下一步便是编写dispatcher-servlet.xml配置文件,首先定义URL的映射方式(HandlerMapping)。Spring提供了几种常用的HandlerMapping。1.使用SimpleUrlHandlerMappingSimpleUrlHandlerMapping提供了最简单的URL映射,通过Properties将URL和Controller对应起来,配置示例如下。<bean id="simpleUrlHandlerMapping" class="org.springframework.web.servlet. handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/a.html">controllerA</prop> <prop key="/b.html">controllerB</prop> </props> </property></bean>这种方式和Struts的配置类似。当用户请求一个URL时,Spring就在SimpleUrl HandlerMapping注入的Properties中查找对应的Controller。2.使用BeanNameUrlHandlerMappingBeanNameUrlHandlerMapping的实现更为简单,每个Controller的URL与其name属性对应,因此,只需要对每个Controller以URL作为name,就可以实现URL映射。配置示例如下。<bean id="beanNameUrlHandlerMapping" class="org.springframework.web.servlet. handler.BeanNameHandlerMapping" /><bean name="/a.html" class="example.chapter7.ControllerA" /><bean name="/a.html" class="example.chapter7.ControllerB" />之所以用Bean的name作为URL而不是id,是因为XML规范不允许在id标识中使用特殊字符“/”。当用户请求一个URL时,Spring将直接查找name为URL的Controller。使用 SimpleUrlHandlerMapping的麻烦之处在于,添加或删除Controller时必须要对 SimpleUrlHandlerMapping做相应的修改,而BeanNameUrlHandlerMapping则无需手工编写映射,只需要在每个 Controller中仔细定义name属性。如果使用XDoclet自动生成配置文件,则可以将name在Controller的注释中定义,维护起来 更加方便。因此,我们推荐首先考虑使用BeanNameUrlHandlerMapping。事实上,如果没有在XML配置文件中定义任何 UrlHandlerMapping,则Spring MVC默认使用BeanNameUrlHandlerMapping。Spring还提供了一个 ControllerClassNameHandlerMapping,它和BeanName UrlHandlerMapping类似,不过是将Controller的ClassName和对应的URL关联起来,由于这种方式灵活性欠佳,实际使用 较少。也可以混合使用多种 UrlHandlerMapping,但是必须为每个UrlHandlerMapping指定order属性来表示优先级,order值越小优先级越高, Spring会先查询优先级高的UrlHandlerMapping。若找到了对应的Controller,就不再继续查询,否则,按照优先级依次查询, 直到找到为止。若所有的UrlHandlerMapping都无法返回一个合适的Controller,并且没有设置默认的Controller时,就会 返回给客户端一个“404 Not Found”错误,表示不存在这个URL。下一步需要为Spring MVC指定一个ViewResolver(视图解析器),指示使用何种视图技术,以及如何解析ModelAndView返回的逻辑视图名称。这里我们直接给出使用JSP视图的配置,对于其他的视图技术将会在后面讲到。<bean id="viewResolver" class="org.springframework.web.servlet.view. InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet. view.JstlView" /> <property name="prefix" value="/" /> <property name="suffix" value=".jsp" /></bean>prefix和suffix将和逻辑视图名称一起组合成为实际视图的路径。例如,对于上例,若返回一个new ModelAndView("test", model),则实际的视图路径由prefix+逻辑视图名+suffix这3部分构成。/test.jsp定义 前缀(prefix)使得视图文件无论放在何处都可以通过修改前缀来实现位置无关性(当然,必须在web目录内),许多应用程序将其放在/WEB-INF 目录下,使得用户无法通过URL直接访问视图文件以保证视图文件的安全;定义后缀(suffix)可以在将来用另一种视图技术(例如,Velocity) 取代现在的JSP视图,只需将后缀从“.jsp”更改为“.vm”即可,而不必更改源代码中的逻辑视图名。总之,一切目标都是为了实现最大限度的解耦。二、实现Controller实现了org.springframework.web.servlet.mvc.Controller接口的Bean都可以作为有效的Controller来处理用户请求。例如,一个最简单的TestController。public class TestController implements Controller { public ModelAndView handleRequest(HttpServletRequest request, HttpServlet Response response) throws Exception { String name = request.getParameter("name"); if(name==null) name = "spring"; Map model = new HashMap(); model.put("name", name); model.put("time", new Date()); return new ModelAndView("test", model); }}注 意,上例的Controller接口和返回值ModelAndView都是在Spring框架中定义的,这和SimpleMVC项目中我们自己定义的 Controller接口和ModelAndView类所在的包是不同的。在SimpleMVC项目中,我们并没有使用Spring MVC,而是借用Spring MVC的概念自定义接口。在现在的SpringMVC项目中,我们没有自定义任何接口,而是直接使用Spring MVC框架来实现Web应用程序,这一点请读者务必区分清楚。另外需要注意的是,ModelAndView返回的逻辑视图是“test”,还记得在viewResolver中定义的prefix和suffix吗?实际的视图名称由这3部分构成便是“/test.jsp”。最后将这个TestController作为Bean定义在dispatcher-servlet.xml中,由于我们准备使用默认的BeanNameUrlHandlerMapping,因此,需要在Bean的name中指定URL。<bean name="/test.html" class="example.chapter7.TestController" />完整的dispatcher-servlet.xml配置文件的内容如下。<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean name="/test.html" class="example.chapter7.TestController" /> <bean id="viewResolver" class="org.springframework.web.servlet.view. InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet. view.JstlView" /> <property name="prefix" value="/" /> <property name="suffix" value=".jsp" /> </bean></beans>注意,我们没有指定UrlHandlerMapping,Spring会自动使用默认的BeanNameUrl HandlerMapping。三、 实现View到目前为止,我们已经编写了 Controller的实现和配置文件,最后一步是编写一个JSP文件作为视图。由于采用了MVC架构,视图的任务只有一个,就是将Controller 返回的Model渲染出来。Spring MVC会将Model中的所有数据全部绑定到HttpServlet Request中,然后将其转发给JSP,JSP只需将数据显示出来即可。通过JSTL标签库能进一步简化显示逻辑,我们看看如何显示TestController返回的Model。test.jsp文件的内容如下。<%@ page contentType="text/html; charset=utf-8" %><%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %><html> <head> <title>SpringMVC</title> </head> <body> <h3>Hello, <c:out value="${name}" />, it is <c:out value="${time}" /></h3> </body></html>现在,Spring MVC所需的所有组件都已编写并配置完毕,我们先来回顾一下Spring MVC的处理流程,如图7-22所示。将SpringMVC工程的编译输出目录设置为/web/WEB-INF/classes,然后编译工程,打开浏览器,测试我们编写的SpringMVC,结果如图7-23所示。图7-23现在,读者对Spring的MVC框架应该有了一个初步的认识。实际上,除了基本的MVC标准流程外,Spring MVC还提供了相当多的功能,下面我们将逐一介绍Spring MVC提供的各种丰富的Controller、拦截器和异常处理机制。