1. 介绍
最近很多同学也开始使用Spring
框架(http://springframework.org)做一些网站了,之前给大家推荐的时候好像都还不感冒,甚至都不知道是什么框架,仍然埋头 Struts
和 Hibernate
的学习。
Spring作为当前J2EE编程实践的替代方案而受到欢迎。实际上,Spring
提供了在J2EE之外的很多通用功能,用于促进良好的编程实践,例如设计接口,单元测试和灵活性(例如,避免依赖性,可配置性)。
Spring
的核心是通过配置文件(通常是XML)来组装组件的系统,这个简单的概念被称为控制反转(IoC),好莱坞原则(“不要找我,我会找你”)和依赖注入。
例如,考虑一下我们的 DAO 对象所需要的Connection
对象,通常,DAO 将在 JNDI 或其他服务中查找此资源。它通常是主动的去从某个地方去“拉”取这个依赖的资源,这种方法强制了 DAO 对于某个特定资源的依赖,必须很明确的知道这个资源来自哪里,怎么实例化(如果是单例的,还要考虑怎么从工厂方法中引入),从而限制DAO的重用性,灵活性和可测试性。当然,可以使用诸如JNDI之类的让这个查找过程简单一点,但仍然必须手动的去管理这些资源的依赖。
那现在,不妨考虑一种场景,如果我们不让 DAO 自己去“拉”这些依赖的资源,反过来,我们通过其它的一些手段将它需要的资源“推”送给它呢,DAO 本身不需要管理这些依赖资源,只需要关注本身的业务逻辑,是不是更通用一些呢。实际上,这个就是Spring
提供的核心理念,让这种资源的依赖管理方式反过来,由Spring
容器来主动的推送资源到目标中去,而不需要目标自己去管理,又被称为“依赖注入”。
Spring
有时被称为轻量级容器,但Spring
实际上非常大(大约有1000个类/接口)。轻量级术语更多地指的是它以最少的依赖性集成技术的能力(与需要实现特定接口的许多J2EE解决方案相反)。
使用依赖注入框架作为基础,Spring
增加了对应用程序功能的许多常见方面的支持,包括持久化,事务控制,AOP,错误处理和分布式计算等。今天主要简单的给大家讲解一下 Spring MVC 相关的知识,好让其它感兴趣的同学能更清晰的认知Spring
,加入到基于Spring
的应用开发过程中来,提高效率。
2. SPRING MVC
和Struts
等其它 MVC 框架类似,Spring
也采用了熟悉的模型 - 视图 - 控制器架构,如各种基于Web的框架所示, 一个Front Controller
Servlet接收系统的所有HTTP请求,并使用定义的映射路由到实际的业务处理程序。这些处理程序通常是Controller
接口的实现,但可以是符合框架规范的任何类。在Spring
MVC 中,DispatcherServlet
作为Front Controller
,调用其他Controller
s。它在处理请求的过程中执行若干其他任务(所有这些都以高度可配置的方式提供),包括视图映射和专门的异常处理。本文仅简要介绍这些领域,重点介绍Controller
s的使用。
控制器类似于Struts Action。它处理请求并返回一个ModelAndView
对象,该对象包含一个表示Model
(用于表示的数据,类似 Map
,实际上它的底层实现就是Map
)和一个表示展现层的View
。Model
大家都很熟悉,也很好理解,View
则是 Spring
的一个抽象概念,表示一个视图对象,Controller
通常会返回一个String
(由可配置解析ViewResolver
将 String
解析成真正的页面)或者实例View
(直接使用)来表示要展示的页面,空返回表示控制器已完成请求的处理,无需额外的View
操作。
Spring
框架中存在许多控制器,如下图所示:
其中SimpleFormController
是这些Controller
类中最常用的。虽然名称翻译过来是简单表单控制器,但SimpleFormController
具有非平凡的生命周期和相关功能 - 正如上面显示的深层继承层次结构所暗示的那样。我们将依次讨论其基类来详细说明其功能。
AbstractController
在将请求处理委托给派生类的handleRequestInternal()
方法之前,提供一些称之为“低级别”的配置和功能。它生成一些诸如控制缓存的头信息cacheSeconds的属性,根据配置拒绝GET或POST请求,并确保在标记为必需时存在会话,还可以在会话上同步请求以防止同一客户端进行多线程访问等。请注意,Spring
与Struts
类似,绝大部分Controller
是单例重复使用的。(或者,ThrowawayController
为每个请求实例化控制器实现。)
BaseCommandController
将请求参数映射到一个配置好的命令类(类似的Struts
的ActionForm
,但它是一个非常简单的JavaBean,没有框架的依赖关系)。有时还会注册一个 Validator 验证器 来检查请求内容。注意,该类并未实现 handleRequestInternal()
(处理实际的业务逻辑),而是将这部分工作留给它的派生类。它定义了 final bindAndValidate()
来将请求参数绑定到Command Object并验证它的内容。提供了几个钩子来定制这个过程:
initBinder(request, binder)
- 可以覆盖以添加PropertyEditors
到binder以处理特殊类型onBind(request, command)
- 在绑定后调用,但在验证之前调用。可以重写以执行专门的映射,正确的映射等。onBindAndValidate(request, command)
- 验证后调用。例如,可以覆盖以执行其他验证。
AbstractFormController
继承了BaseCommandController
并覆盖handleRequestInternal()
用于处理GET和POST请求。此类将Command Object(from BaseCommandController
)称为表单对象,因此下面将使用此术语。对于POST请求,可以创建表单(命令)对象的默认实例,也可以从会话中检索预先存在的实例(然后删除)。派生AbstractWizardFormController
利用会话方法进行多屏输入。会话范围的Command Object也可以用作通过将控制器配置为要求其存在来防止重复表单提交的手段(有关详细信息,请参阅相关的javadocs)。可以通过覆盖formBackingObject()
方法来定义初始Command Object。
接下来,使用请求参数填充表单对象BaseCommandController.bindAndValidate()
,AbstractFormController
没有定义上面提到的任何相应的抽象方法(initBind
等等)。最后,业务逻辑处理被委托给抽象方法:
1 | ModelAndView processFormSubmission(HttpServletRequest request, |
通过获取Command Object(表单)开始GET请求处理formBackingObject()
。这可以被覆盖以提供填充有来自数据库的数据的对象。如果启用,则请求参数然后绑定到Command Object对象,(同样,initBinder()
可以专门处理)。最后,处理委托给:
1 | protected abstract ModelAndView showForm( |
具体的类SimpleFormController
又增强了一些AbstractFormController
功能。可以配置表单和成功视图 - 验证失败将用户返回到表单视图,同时成功验证和后续操作将导致成功视图。通常,派生类只需要覆盖几种onSubmit()
方法中的一种来处理表单提交(例如保存到数据库)。
3. MVC 示例
现在我们来测试验证一下。我们将创建一个简单的表单,用户输入他们的名字并选择他们喜欢的水果。
3.1 实体对象:Fruit
Fruit类是一个相当标准的JDK 1.5 Java枚举类型:
1 | public class Fruit { |
当然,这也可以作为查找表存储在数据库中。由于这些对象对应于HTML <OPTION>
标签的字符串值属性,因此主要问题是将下拉列表映射到服务器管理的对象列表。
要将基于字符串的引用转换为枚举值(反之亦然),我们定义一个专门的java.beans.PropertyEditor
:
1 | private class FruitSupport extends PropertyEditorSupport { |
3.2 Command (Form) 类
我们还需要一个JavaBean来表示表单的内容(名称和结果):
1 | public class FormBean { |
注意,这是一个非常非常简单的 JavaBean ,除了属性及 Getter/Setter外没有任何其它的依赖。
3.3 Spring 配置
首先我们添加DispatcherServlet
到web.xml
中,来接管所有的 HTTP 请求(这个就是我们之前提到的 Front Controller Servlet):
1 | <servlet> |
请注意,所有.htm
URL都将路由到Spring
servlet。除了配置MVC架构的其他方面之外,该spring-servlet.xml
文件还定义了DispatcherServlet
委托给各种处理程序(此处为 Controller
)的映射。下面的部分定义了Controller
相关的类及其到URL的映射。
1 | <bean id="formController" |
实例MyFormController
给出一个idformController
,并由urlMapping
bean 中的id引用,以将URL映射/form.htm
到控制器实例。MyFormControllerr
的属性如下:
commandName
定义Command Object的 idcommandClass
将我们的表单bean定义为FormBean
的实例formView
如果表单提交失败,则定义要调用的视图successView
如果表单提交成功,则定义要调用的视图
请注意,这个例子中formViews
和uccessView
引用了相同的视图,实际的场景中通常不是这样的,这里只是举例演示用法。
3.4 Controller
我们演示用的MyFormController
比较简单,扩展SimpleFormController
并定义了一个默认构造函数来支持Spring
实例化。实现initBinder()
方法注册PropertyEditor
到binder对象以支持我们前面提到的Fruit
枚举类型。referenceData()
方法获取所有Fruit实例,并将它们放在具有唯一键的映射中,这些内容将在 JSP 页面中使用。
最后,该onSubmit()
方法处理来自表单Command Object的数据。虽然存在许多更简单的onSubmit()
方法并且可以被覆盖,但在这种情况下需要完整的参数集(Request
,Response
等)。复杂性在于返回表单页面,同时保持提供的项目列表referenceData()
。在成功提交表单时,不会调用此方法,从而产生一个空列表。
1 | public class MyFormController extends SimpleFormController { |
3.5 JSP 页面
最后,JSP页面用于/form.htm
定义View
。Spring
为许多不同的View选项提供了轻松集成,包括JSP,XSLT,Velocity和FreeMarker。视图可以很容易地与不同的处理程序集成。关于JSP,Spring
只支持少量自定义标签,只有一个spring:bind
是常用的。我们写的form.jsp
页面同时演示了它与JSTL的用法。
本质上,spring:bind
标记创建一个本地上下文,其中 status
变量与HTML标签中引用的变量的绑定相关联。 status
包含上次验证尝试的结果,值绑定和属性的标识符。
1 | <form method="post"> |
4. 摘要
本文仅涉及SpringMVC
框架的简单使用方式,并没有涉及 Spring
的其它部分,实际上Spring
在系统的其他层提供了许多好处,包括与JDO和Hibernate的集成。作为一种通用方法,它促进并简化了测试驱动开发(TDD),它促进了许多有益的设计和开发实践。
Spring
正在非常积极的发展和工具整合不断改进。例如,计划的增强功能包括与JSF,JMX和AspectJ的新集成或改进集成。XDoclet支持是可用的,还有一个Eclipse插件。鼓励大家学习研究一个这个Spring
框架。