第21章 Spring 的MVC框架 Spring不但一个很好的集成框剪,它还提供了构建Web应用程序的全功能MVC模块。Spring 的MVC框架非常强大并不逊色于其他专业的Web框架,如Struts、WebWork。但在国内Spring MVC应用的并不是特别多,原因可能是Struts太深入人心的缘故吧。 虽然Spring MVC并不是本书的重点,但了解它的结构和原理也是十分有好处的。
21.1 Spring MVC简介 Spring的MVC Web 框架是高度可配置的,而且包含多种视图技术,例如JSP、Velocity、Tiles和iText。Spring MVC 分离了控制器、模型对象、分派器以及处理程序对象的角色,这种分离让它们更容易进行定制。
21.1.1 Spring MVC的结构 Spring的web框架是围绕DispatcherServlet来进行设计的。DispatcherServlet的作用是将请求分发到不同的处理器。Spring的web框架包括可配置的处理器(handler)映射、视图(view)解析、本地化(local)解析、主题(theme)解析以及对上传文件解析。处理器是对Controller接口的实现,该接口仅仅定义了ModelAndView handleRequest(request, response)方法。可以通过实现这个接口来生成自己的控制器(也可以称之为处理器),但是从Spring提供的一系列控制器继承会更省事,比如AbstractController、AbstractCommandController和SimpleFormController。
21.1.2 Spring Web MVC框架的特点 Spring Web MVC框架提供了大量独特的功能,包括: 清晰的角色划分:控制器(controller)、验证器(validator)、命令对象(command object)、表单对象(form object)、模型对象(model object)、Servlet分发器(DispatcherServlet)、处理器映射(handler mapping)、视图解析器(view resolver)等等。 每一个角色都可以由一个专门的对象来实现。 强大而直接的配置方式:将框架类和应用类都作为JavaBean配置,支持在一个context中引用其他context的中JavaBean,例如,在web控制器中对业务对象和验证器(validator)的引用。
21.1.2 Spring Web MVC框架的特点 可重用的业务代码:可以使用现有的业务对象作为命令或表单对象,而不需要在类似ActionForm的子类中重复它们的定义。 可定制的绑定(binding) 和验证(validation):比如将类型不匹配作为应用级的验证错误,这可以保存错误的值。再比如本地化的日期和数字绑定等等。在其他某些框架中,只能使用字符串表单对象,需要手动解析它并转换到业务对象。 可定制的handler mapping和view resolution:Spring提供从最简单的的URL映射,到复杂的、专用的定制策略。与某些MVC框架强制开发人员使用单一特定技术相比,Spring显得更加灵活。灵活。
21.1.2 Spring Web MVC框架的特点 灵活的model转换:在Springweb框架中,使用基于Map的名/值对来达到轻易地与各种视图技术的集成。 可定制的本地化和主题(theme)解析:支持在JSP中可选择地使用Spring标签库、支持JSTL、支持Velocity(不需要额外的中间层)等等。 Spring Bean的生命周期可以被限制在当前的HTTP Request或者HTTP Session。准确的说,这并非Spring MVC框架本身特性,而应归属于Sping MVC使用的WebApplicationContext容器。
21.2 DispatcherServlet的功能 和其它web框架一样,Spring的web框架是一个请求驱动的web框架,其设计围绕一个中心的servlet进行,它能将请求分发给控制器,并提供其它功能帮助web应用开发。然而,Spring的DispatcherServlet所做的不仅仅是这些,它和Spring的IoC容器完全集成在一起,从而允许使用Spring的其它功能。
21.2.1 在Web服务器中配置DispatcherServlet DispatcherServlet顾名思义实际上还是一个Servlet。和其它Servlet一样,DispatcherServlet定义在web应用的web.xml文件里。DispatcherServlet处理的请求必须在同一个web.xml文件里使用url-mapping定义映射。如代码21-1所示演示了如何配置DispatcherServlet。
21.2.2 加载Bean配置文件 当DispatcherServlet配置好以后,DispatcherServlet接收到与其对应的请求之时,处理就开始了。下面的列表描述了DispatcherServlet处理请求的全过程: (1)找到WebApplicationContext并将其绑定到请求的一个属性上,以便控制器和处理链上的其它处理器能使用WebApplicationContext。默认的属性名为DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE。 (2)将本地化解析器(localResolver)绑定到请求上,这样使得处理链上的处理器在处理请求(准备数据、显示视图等等)时能进行本地化处理。若不使用本地化解析器,也不会有任何副作用,因此如果不需要本地化解析,忽略它就可以了。
21.2.2 加载Bean配置文件 (3)如果上传文件解析器被指定,Spring会检查每个接收到的请求是否存在上传文件,如果是,这个请求将被封装成MultipartHttpServletRequest以便被处理链中的其它处理器使用。 (4)找到合适的处理器,执行和这个处理器相关的执行链(预处理器,后处理器,控制器),以便为视图准备模型数据。 (5)如果模型数据被返回,就使用配置在WebApplicationContext中的视图解析器显示视图,否则视图不会被显示。有多种原因可以导致返回的数据模型为空,比如预处理器或后处理器可能截取了请求,这可能是出于安全原因,也可能是请求已经被处理过,没有必要再处理一次。
21.2.3 Spring Web应用的上下文 WebApplicationContext仅仅是一个拥有web应用必要功能的普通ApplicationContext。它与一个标准的ApplicationContext的不同之处在于,它能够解析加载主题,并且它知道自己与哪个servlet相关联(通过ServletContext)。WebApplicationContext被绑定在ServletContext上,当需要的时候,可以使用RequestContextUtils提供的静态方法找到WebApplicationContext。 Spring的DispatcherServlet有一组特殊的bean,用来处理请求和渲染相应的视图。这些bean包含在Spring的框架里,可以在WebApplicationContext中配置,配置方式与配置其它bean相同。这些bean中的每一个都在下文作详细描述。此刻读者只需知道它们的存在,便继续对DispatcherServlet进行讨论。对大多数bean,Spring都提供了合理的缺省值,所以在开始阶段,不必担心如何对其进行配置。
21.3 控制器(Controller) 控制器的概念是MVC设计模式的一部分,在其他Web框架中常常被称为Action,而在Spring MVC中叫做Controller。应用程序的行为通常被定义为服务接口,而控制器使得用户可以访问应用所提供的服务。控制器解析用户输入,并将其转换成合理的模型数据,从而可以进一步由视图展示给用户。Spring以一种抽象的方式实现了控制器概念,这样使得不同类型的控制器可以被创建。Spring本身包含表单控制器、命令控制器、向导型控制器等多种多样的控制器。 可以发现Controller接口仅仅声明了一个方法,它负责处理请求并返回合适的模型和视图。虽然Controller接口是完全抽象的,Controller接口仅仅定义了每个控制器都必须提供的基本功能:处理请求并返回一个模型和一个视图。但Spring也提供了许多可能会用到的控制器。可以直接使用或者继承它们来实现需要个功能
21.3.1 基类AbstractController 为了提供一套基础设施,所有的Spring控制器都继承了AbstractController ,AbstractController 提供了诸如缓存支持和mimetype设置这样的功能。如表所示累出的AbstractController包含的功能。
21.3.2 其他实用的控制器 除了从AbstractController继承来实现Controller外,Spring还提供了一批实用的Controller,供用直接使用。下面仅介绍一些比较常用。 (1)MultiActionController将多个行为(action)合并在一个控制器里,这样可以把相关功能组合在一起。 (2)CommandControlle.rSpring的是Spring MVC的重要部分。 (3)SimpleFormController --这是一个form cotnroller,当需要根据命令对象来创建相应的form的时候,该类可以提供更多的支持。
21.4 处理器映射(handler mapping) 通过处理器映射可以将web请求映射到正确的处理器(handler)上。HandlerMapping的基本功能是将请求传递到HandlerExecutionChain上。首先,这个HandlerExecutionChain必须包含一个能处理该请求的处理器。其次,这个链也可以包含一系列可以拦截请求的拦截器。当收到请求时,DispatcherServlet将请求交给处理器映射,让它检查请求并找到一个适当的HandlerExecutionChain。然后,DispatcherServlet执行定义在链中的处理器和拦截器(interceptor)。
21.5 视图解析器 视图解析器是控制器之后的处理过程,是控制器与视图之间的桥梁。Spring Web框架的所有控制器都返回一个ModelAndView实例。Spring提供了视图解析器供在浏览器显示模型数据,而不必被束缚在特定的视图技术上。Spring内置了对JSP,Velocity模版和XSLT视图的支持。 ViewResolver是Spring的视图处理方式中特别重要的接口。ViewResolver提供了从视图名称到实际视图的映射。View处理请求的准备工作,并将该请求提交给某种具体的视图技术。Spring为多种视图都提供了不同的解析器。
21.6 Spring对视图的支持 与其他Web框架一样Spring对视图也提供了很多支持,如Spring的标签库、主题、支持其他模板。这些概念在本书的前一篇Struts2中已经有过介绍,相信读者不会十分陌生。
21.6.1 Spring的标签库 Spring的表单标签库存在spring.jar中。这个库的描述文件(descriptor)是 spring-form.tld。如果想使用这些标签,可以在JSP代码的起始部分加入下面这行声明,form 是这个标签库所提供标签的前缀名。 <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
21.6.2 Spring的主题 主题的概念并不陌生,在本书的Struts2篇已经介绍过。在Spring中为了在web应用中使用主题,需要设置org.springframework.ui.context. ThemeSource。WebApplicationContext是从ThemeSource扩展而来,但是它本身并没有实现ThemeSource定义的方法,它把这些任务转交给别的专用模块。如果没有明确设置,真正实现ThemeSource的类是org.springframework.ui.context.support.ResourceBundleThemeSource。这个类在classpath的根部(比如在/WEB-INF/classes目录下)寻找合适的属性文件来完成配置。
21.7 MVC的完整实例 本节将以一个完整实例还演示Spring MVC的使用方法。示例银行应用程序允许用户根据惟一的 ID 和口令查看帐户信息。采用JSP 技术作为视图页面。这个简单的应用程序包含一个视图页用于用户输入,另一页显示用户的帐户信息。
21.7.1 配置Web.xml 所有的Web应用都要从配置Web.xml开始。Struts是如此,那么Spring的Web模块也不例外,是通过一个Servlet将框架启动的。
21.7.2 创建Controller 从LoginBankController 开始,它扩展了 Spring MVC 的 SimpleFormController。SimpleFormContoller 提供了显示从 HTTP GET 请求接收到的表单的功能,以及处理从 HTTP POST 接收到的相同表单数据的功能, LoginBankController用AuthenticationService和AccountServices服务进行验证,并执行登录活动。AuthenticationService 类处理银行应用程序的验证。AccountServices 类处理典型的银行服务,例如查找交易和电汇。代码21-9描述了如何把AuthenticationService和AccountServices连接到 LoginBankController。
21.7.3 视图解析器 Spring MVC 的视图解析器把每个逻辑名称解析成实际的资源,即包含帐户信息的JSP文件。这里使用的是InternalResourceViewResolver,在JSP页面中使用了JSTL标记,所以用户的登录名称解析成资源/jsp/login.jsp,而viewClass成为JstlView。 代码 配置视图解析器:sampleBanking-services.xml <!—定义视图解析器--> <bean id="view-Resolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass"> <value>org.springframework.web.servlet.view.JstlView</value> </property> <property name="prefix"><value>/jsp/</value></property> <property name="suffix"><value>.jsp</value></property> </bean>
21.7.4 JSP页面 本实例只包含两个JSP页面,一个登录界面如代码所示,
21.7.5 发布于测试 与一般的Web程序一样,需要在Tomcat的发布目录webapps下创建一个Web目录。本例中命名为“springbanking”,目录中包含WEB-INF/classes,例子中类文件编译后应该在此目录。在把Spring需要的jar文件加入到WEB-INF/lib下。工程需要的配置文件sampleBanking-services.xml、sampleBankingServlet-servlet.xml、web.xml都应该在WEB-INF目录下。目录结构如图所示。
21.8 用注释来驱动Spring MVC(2.5新增) 继Spring 2.0对Spring MVC进行重大升级后,Spring 2.5又为Spring MVC引入了注释驱动功能。现在无须让Controller继承任何接口,无需在 XML 配置文件中定义请求和Controller的映射关系,仅仅使用注释就可以让一个POJO具有Controller的绝大部分功能。Spring MVC框架的易用性得到了进一步的增强.在框架灵活性、易用性和扩展性上,伴随着Spring一路高唱猛进,可以预见Spring MVC 在MVC市场上的吸引力将越来越不可低估。
21.8.1 基于注释的Controller 如前面章节中介绍的,当创建一个Controller时,需要直接或间接地实现 org.springframework.web.servlet.mvc.Controller 接口。一般情况下,是通过继承 SimpleFormController 或 MultiActionController 来定义自己的 Controller 的。在定义 Controller 后,一个重要的事件是在 Spring MVC 的配置文件中通过 HandlerMapping 定义请求和控制器的映射关系,以便将两者关联起来。
21.8.2 使注释生效 为了让基于注释的 Spring MVC 真正工作起来,需要在 Spring MVC 对应的bean配置文件中增加一些支持,注意XML文件头中命名空间的不同定义。 在文件中配置了一个AnnotationMethodHandlerAdapter,它负责根据Bean中的Spring MVC注释对Bean进行加工处理,使这些Bean变成控制器并映射特定的 URL 请求。 还需要定义模型视图名称的解析规则,这里使用了Spring2.5的特殊命名空间,即p命名空间,它将原先需要通过<property>元素配置的内容转化为<bean>属性配置,在一定程度上简化了<bean>的配置。
21.9 小结 本章介绍了Spring的MVC框架,它同样是Spring重要的一部分,Spring的MVC框架结构与其他Web框架结构上应该说是一致的,包含分发器、控制器、映射关系配置、视图支持等几个部分。只是个别称谓上有所区别,如Struts和Webwork中的Action变成了Controller。 Spring的MVC框架受争议之处是它的结构不是特别清晰。Spring MVC虽然灵活但使用起来有点复杂。如果对Spring的IoC和AOP有深刻了解之后对MVC框架就不会特别陌生了正如本章所描述的,随着Spring越来越深入人心,Spring的MVC框架也越来越受重视。