Web应用开发早期曾经是那么的“简单”,那个时候还只是纯HTML页面和浏览器特效而已。由于还涉及不到动态数据操作和商业应用,也就省去了很多麻烦。但是这样的“简单”只是过眼云烟,如今我们不得不为复杂的基于Web的商业应用开发采用诸多技术。
本文将介绍如何利用Struts进行应用开发的前台整合的开发过程。Struts是一个为开发基于模型(Model)-视图(View)-控制器(Controller)(MVC)模式应用架构的开源框架,是利用Java Servlet和JSP构建Web应用的一项非常有用的技术。
阅读本文需要读者具有以下几方面的开发经验:JSP、Servlet、自定义标签库(custom tag library)和XML。如果读者想补一补自定义标签库的知识,可以参考作者以前关于这方面的文章。而本文也是关于介绍如何使用Struts系列文章的上半部分,本系列暂定分为上下两部分。
新手上路注意事项
Struts是一个基于Sun J2EE平台的MVC框架,主要是采用Servlet和JSP技术来实现的。其最初萌芽于Craig McClanahan的构思,诞生至今也一年有余了。现在,Struts是Apache软件基金会旗下Jakarta项目组的一部分,其官方网站是http://jakarta.apache.org/struts。由于Struts能充分满足应用开发的需求,简单易用,敏捷迅速,在过去的一年中颇受关注。Struts把Servlet、JSP、自定义标签和信息资源(message resources)整合到一个统一的框架中,开发人员利用其进行开发时不用再自己编码实现全套MVC模式,极大的节省了时间,所以说Struts是一个非常不错的应用框架。
目前的Struts 1.0修正版包括完整的文档,既可以说是用户文档又是开发指导文档。如果读者是JSP新手,或者对MVC设计模式不是太熟的话,可能刚上路时会比较慢,不过不用担心,要相信自己会尽快赶上的:)
此外,应该注意到尽管当前Struts只是1.0版,但已经相当稳定了,作者从Struts 0.9版就在一个大规模的项目中应用了(最近升级到1.0版),至今还没有遇到什么麻烦问题。实际上,Struts在这个要开发复杂用户界面的项目中,为我们团队大大的缩短了开发时间,在此衷心的感谢Struts项目团队的所有开发人员。
开始上路!
Struts框架可分为以下四个主要部分,其中三个就和MVC模式紧密相关:
1、模型(Model),本质上来说在Struts中Model是一个Action类(这个会在后面详细讨论),开发者通过其实现商业逻辑,同时用户请求通过控制器(Controller)向Action的转发过程是基于由struts-config.xml文件描述的配置信息的。
2、视图(View),View是由与控制器Servlet配合工作的一整套JSP定制标签库构成,利用她们我们可以快速建立应用系统的界面。
3、控制器(Controller),本质上是一个Servlet,将客户端请求转发到相应的Action类。
4、一堆用来做XML文件解析的工具包,Struts是用XML来描述如何自动产生一些JavaBean的属性的,此外Struts还利用XML来描述在国际化应用中的用户提示信息的(这样一来就实现了应用系统的多语言支持)。
好,下一步咱们来看看构成这个框架的各个部分以及相互之间是怎样运作的吧!
配置
使用Struts之前,咱们必先设置好JSP服务器,以便让服务器在用户请求时,知道该如何将指定后缀的请求转到相应的Controller-Struts ActionServlet处理,当然,这些配置信息都一般在服务器启动时通过web.xml文件读入的。我们可以在web.xml定义多个Controlloer,为每一个应用定义一个。一个典型的web.xml文件配置如下,其中有相应的注释,很好懂的,在后面讨论Action的时候,我们将主要分析strutc-config.xml。
〈web-app〉
〈servlet〉
〈!--
以下配置信息声明了Struts中的ActionServlet,即一个名为OreillyAction的
Servlet,其具体实现为org.apache.struts.action.ActionServlet。
在这个配置中还有这个Servlet的两个参数:debug level和detail,
此处这两个参数的值都设为了2,此外还设置了在启动载入时创建两个实例。
--〉
〈servlet-name〉OreillyActionServlet〈/servlet-name〉
〈servlet-class〉org.apache.struts.action.ActionServlet
〈/servlet-class〉
〈init-param〉
〈param-name〉config〈/param-name〉
〈param-value〉/WEB-INF/struts-config.xml〈/param-value〉
〈/init-param〉
〈init-param〉
〈param-name〉debug〈/param-name〉
〈param-value〉2〈/param-value〉
〈/init-param〉
〈init-param〉
〈param-name〉detail〈/param-name〉
〈param-value〉2〈/param-value〉
〈/init-param〉
〈load-on-startup〉2〈/load-on-startup〉
〈/servlet〉
〈!--
设置所有后缀为.action的请求,都转发到OreillyActionServlet
--〉
〈servlet-mapping〉
〈servlet-name〉 OreillyActionServlet 〈/servlet-name〉
〈url-pattern〉*.action〈/url-pattern〉
〈/servlet-mapping〉
〈!--
将初始请求页面设置为login.jsp
--〉
〈welcome-file-list〉〈welcome-file〉login.jsp〈/welcome-file〉
〈/welcome-file-list〉
〈!--
设置Struts的JSP页面要用到的标签库和她们的路径
--〉
〈taglib〉
〈taglib-uri〉/WEB-INF/struts-bean.tld〈/taglib-uri〉
〈taglib-location〉/WEB-INF/struts-bean.tld〈/taglib-location〉
〈/taglib〉
〈taglib〉
〈taglib-uri〉/WEB-INF/struts-html.tld〈/taglib-uri〉
〈taglib-location〉/WEB-INF/struts-html.tld〈/taglib-location〉
〈/taglib〉
〈taglib〉
〈taglib-uri〉/WEB-INF/struts-logic.tld〈/taglib-uri〉
〈taglib-location〉/WEB-INF/struts-logic.tld〈/taglib-location〉
〈/taglib〉
〈/web-app〉
控制器(Controller)
Controller是这个框架中扮演“交通警察”的角色,当客户端与服务器有交互动作时,都由她来控制。Controller将HTTP请求封包并转发到框架中相应的对象,这些对象可能是一个JSP页面或一个Action。
Controller在web.xml中设置为org.apache.struts.action.ActionServlet的一个实例,在本例中,这个实例就是OreillyActionServlet。在一个完整的控制过程中,也就是处理一个HTTP请求时,在控制过程之初,这个Servlet会从一个配置文件struts-config.xml中获取请求与控制动作向对应的配置信息,这个我们会在后面详细讨论,Controller通过这些配置信息来决定HTTP请求该往何处转发,而这些Action在接收到转发来的请求后,实现真正的商业逻辑。我们要注意的非常重要的一点是Action对象要能够调用这个ActionServlet的若干方法,通过这个有力的特性,当Action对象在控制过程中将请求再向别的Action对象转发时(最初的请求是由ActionServlet获取,向Action对象转发,而Action对象还可以再转发到别的对象),我们可以将一些需要共享的数据对象通过调用一些方法放入这个Servlet相关的一些标准容器中捎带过去。
模型(Model)
所谓Model就是在对用户请求的整个控制过程中,真正处理用户请求并保存处理结果的对象,在整个过程中,我们一般利用JavaBean来把一些信息保存起来以便在各个对象之间传递。因为在框架中,Model对象是真正处理商业逻辑功能的对象,因此也就是框架中应用需求实现相关性最大的部分。在Struts的实现里,Model的具体表现形式就是ActionForm对象和与其对应的Action对象了。对用户提交表单的数据进行校验,甚至对数据进行预处理都能在ActionForm中完成。通常的应用中,一般是一个Model对象和一个请求页面对应的关系,但也可以一个Model对象对应多个页面请求。如果struts-config.xml配置文件没有指定一个Model对象对应的Action,那么控制器将直接把(通过Model对象完成数据封装的)请求转到一个View对象。
struts-config.xml
前面多次提到的struts-config.xml配置文件是整个框架的主心骨。web.xml文件定义了一个请求到来应向何处转发后,后面的工作就全权由struts-config.xml管理控制了。可以说struts-config.xml就是整个Struts框架的“扛把子”,只有这位“老大”清楚所有请求与动作的映射关系,要是他哪里没有搞定或不爽的话,整个“社团”就什么也摆不平了:)如今的应用系统,XML形式的配置文件越来越多,如果整个系统只使用一个这样的配置文件的话,那么保持整个系统的模块化和可维护性都非常的轻松。使用配置文件来描述请求-动作的控制过程和相互关系,而不是在代码中将对象之间的调用关系写死,那么都应用系统有变动时,我们只用修改配置文件就行了,而不是再重新编译发布程序了。
Controller通过struts-config.xml文件的配置信息确定当有请求时应该调用那个对象来处理,从效率的角度出发,这些信息都是在系统启动时读入并存在内存中的。下面我们将讲解一个极短小的struts-config.xml文件,文件中定义了一个与登录请求对应的登录动作,请求到达后将被转发到com.oreilly.ui.authentication.actions.LoginAction这个Action对象,该对象处理的结果决定向用户返回的页面。这个例子同时还示范了一个Action对象将请求转发到别的Action对象,而例子中另一个返回的对象则是一个View对象,即我们看到的login.jsp页面。
〈?xml version="1.0" encoding="UTF-8" ?〉
〈!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.0//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_0.dtd";〉
〈struts-config〉
〈!-- ========== Action 映射定义 =================== --〉
〈action-mappings〉
〈!-- 〈action〉 属性的说明 --〉
〈!-
type - 完整的Action实现类名
name - 该Action要用到的ActionForm名
path - 请求该Action的URI
unknown – 如果将该属性设置为true,那么就是声明这个Action将处理整个应用中
所有未找到相应处理Action的请求,当然,一个应用系统中也只会有一个Action
的unknown属性可以设为true了
validate - 如果本属性为true则在Action动作之前其对应的ActionForm
的validate方法会自动被调用,一般用以校验用户输入的数据
--〉
〈!-- ~~~~~~~~~~~~~~~~~~~~~ --〉
〈!-- O'Reilly Main Actions --〉
〈!-- ~~~~~~~~~~~~~~~~~~~~~ --〉
〈action path="/Login"
type="com.oreilly.ui.authentication.actions.LoginAction"〉
〈forward name="success" path="/DisplayMainPage.action"/〉
〈forward name="failure" path="/login.jsp"/〉
〈/action〉
〈/action-mappings〉
〈/struts-config〉
视图(View)
View对象通常来说都是指的JSP页面。Struts框架实际上并没有真正的JSP的要求规范,而是提供了大量的标签库来为开发者更简便的将JSP整合到Struts框架中。在Struts中通过标签定义方式的JSP页面能够将用户通过表单输入的数据存入一个JavaBean中,也就是我们前面提到的ActionForm bean。通过Action类调用(自动或手动)ActionForm的校验方法来检查用户输入的数据,如果发现不合法的数据,再通过Struts的一个通用机制将错误信息返回给用户显示。
Struts框架提供了若干个标签库,它们有各自不同的用途。由于这些库还可以脱离Struts框架单独使用,这样我们也可以在其他系统中尝试使用这些标签库,它们包括:
* struts-html - 这个标签库用来创建动态的HTML页面和表单。
* struts-bean - 提供了类似甚至更强于中的功能。
* struts-logic - 用于在页面输出文本信息时的条件、循环等流程的控制。
* struts-template - 用于产生有共同风格的动态JSP页面模板。
此外,可定制标签库在Struts中还有一大用处是,通过资源文件的方式来实现应用系统的多语言特性,应用Struts的系统若想将系统中的用户交互信息换一种语言的会很简单,更换一个不同的资源文件就可以了。
大家都开始应用Struts吧!
Struts框架可能对于大多数开发人员来说,是一门比较新的技术。但我们现在已经可以在不少的应用系统中看到Struts的身影了,而我们大可在新的应用或正在开发的JSP项目中使用Struts框架。
例如,在作者现在正在为客户开发的一个大型数据库应用系统中,商业逻辑都是通过EJB来实现的,用户界面则是JSP页面。在struts-config.xml文件中定义了用户输入表单和对应的Action类,当一个请求发生时,即用户数据以ActionForm的形式封装提交到Action时,Action先调用ActionForm的校验方法,数据检查校验通过后,Action再调用相应的EJB中的方法来完成数据操作,操作的结果以XML的形式返回,XML解析后再放入我们数据的封装传递JavaBean - ActionForm中显示到JSP页面里返回用户。
整个的控制流程(包括Action调用后的不同的返回结果)都尽在struts-config.xml中所掌握,这种“中央集权”的方式非常便于应用流程的调整。而不管是Servlet还是JSP页面中(甚至在一些n层的应用架构)都无需撰写如何获取显示数据的代码。
由于目前作者所开发的是一个较大型的系统,有很多的JSP页面和用户要提交的ActionForm类型,因此发现Struts的一个麻烦的地方,那就是:我们要为如此多页面和ActionForm开发对应的Action类来完成控制,因为我们目前JSP和ActionForm与Action是一对一的关系。不过我认为如果在项目前期分析和设计时多下些功夫,做出更完美一些的设计方案的话,这样的情况是可以避免的,当然,在新产品的开发过程中,想一步就把所有需求弄清楚明白那也是不可能的。我们不是都有这样的经历吗?在开发中的应用系统正一步一步走向成熟的时候,更新和更明确的需求才会被提出来。不过,像我们手里这个利用Struts开发了六个月的系统也确实少见了,呵呵。除去这些非技术因素不谈,Struts框架为我们实现MVC模式节省了大量的时间,并且开发出的系统相当的稳定,可以说是很成熟的产品了。
在本系列文章的第二部分,我们将把各小段代码集成起来,完成一个完整的Struts应用的实例,希望大家继续和作者一起学习Struts!
注:Sue Spielman是ONJava.com的副编辑,主要擅长于JSP和Servlet技术,她还是Switchback Software LLC公司的总裁和高级技术咨询专家。