CSDN博客

img Explorerwen

一个掌握Struts企业级Web开发框架的实例

发表于2004/10/28 15:47:00  962人阅读

 Struts是源代码开放的企业级Web应用开发框架,它的设计目的是从整体上减轻构造企业Web应用的负担。本文通过一个Struts应用的实例,帮助你迅速掌握Struts。

  Struts是在Jakarta项目下开发的源代码开放软件,由一系列的框架类、辅助类和定制的JSP标记库构成,定位在基于Model 2设计模式的J2EE应用开发。Model 2体系是MVC(Model-View-Controller)体系的一种应用。在MVC体系中,数据模型(Model)、表现逻辑(View)和控制逻辑(Controller)是分离的组件,但它们可以互相通信。Struts力图从整体上减轻构造企业级Web应用的负担,并提供国际化和数据库连接池支持。

  Struts体系可以看成两个相对独立的部分:第一个部分是Struts API,用于编写支持Struts的应用组件;第二部分是Struts的JSP标记库,由html、bean、logic和template四个标记组成。Struts的两个部分有着各自不同的用户。对于规模较小的项目,同一个用户可能同时使用这两个部分;但对于规模较大的项目,通常开发者使用API组件,而负责HTML页面布局的人使用标记库。

  Struts的设计目标是为Model 2 Web应用开发提供一个强大的框架。同时,Struts还包含了一些实用组件,例如Digest,但这些组件并不从属于上面提到的两个部分。

  Struts应用的体系结构

  对于从传统编程环境转入Web开发的人来说,Web编程中令人很不习惯的一个特点是缺乏“程序”。传统的应用总是有主入口点、流程控制和出口点。但在Web网站上,用户可能从任何地方进入,按照一种完全随机的次序访问各个页面,甚至可能跳过多个页面,也可能在一、两个小时内毫无动静。这是HTTP访问的基本特征,无论是Struts还是其他Web编程框架,都无法改变这一点。然而,Struts能够隐藏Web访问固有的“混乱”,帮助开发者建立起清晰和明确的秩序和规则。

  在Struts应用中,有一个称为ActionServlet的主调度程序(或称为分配器),如图1所示。不过,并非所有的请求都必须通过ActionServlet。用户的请求目标可以是非Struts的页面,也可以是那些使用了Struts标记库但不使用Struts请求分配服务的页面。这正是Struts体系的优点之一:按需使用。许多编程框架要求你要么不用,要么全部使用,而且一旦你决定使用,以后要悔改从前的错误就会付出高昂的代价。Struts按需使用的优点与这类系统形成了强烈对比。

  

  图1 Struts框架中的请求处理

  Struts应用由下面这些基本模块构成:

  1.配置信息;

  2.Servlet,主要是Struts的ActionServlet;

  3.动作类(Action),执行逻辑和控制(请求分配)功能,它们由ActionServlet调用;

  4.JSP页面(属于View),常常通过动作类分派;

  5.JSP标记库,根据需要使用;

  6.各种形式的JavaBean,包括用户定义的JavaBean。

  典型的Struts应用要用到三种配置文件:web.xml、struts-config.xml和可选的应用资源文件。

  web.xml是Web应用的标准配置文件,是所有J2EE Web应用必需的组成部分。应用服务器通过该配置文件把URL映射到Servlet和JSP,通过该配置文件为Servlet和JSP指定启动参数。为Struts应用提供的基本web.xml文件很简单,真正必需的只有一个主ActionServlet定义,以及一个确保Struts请求传递到ActionServlet的映射。按照惯例,以“.do”结尾的URL都是Struts请求,例如/login.do。应用服务器利用web.xml文件中的映射,把该请求传递给ActionServlet。接着,ActionServlet决定如何分配该请求。ActionServlet的决定依据是struts-config.xml中定义的规则,和/或是通过ActionServlet派生类额外定义的分配逻辑。

  struts-config.xml称为Struts配置文件。Struts应用是一个依靠struts-config.xml文件把组件连接起来的网络。struts-config.xml文件为Web应用的组件定义了逻辑名称,也定义了它们在Struts框架下的属性和关系,就像web.xml文件在Web应用框架之内定义组件一样。struts-config.xml文件包含了与Struts框架有关的应用信息,这些信息分四个类:

  1.数据源信息,它是可选的。在这里可以指定一个或者多个JDBC数据源,使得数据库定义信息集中化。对于数据库访问,Struts还有一个额外的优点,即支持基本的数据库连接池功能。

  2. Form Bean是JavaBean的一种特殊类型,它简化了Web表单的处理。

  3. Global Forwards是全局性的转发定义信息。Struts动作按照一种“请求—转发”机制运行。为了最大限度地分  离动作模块与转发目标,这里使用了一种映射机制,允许通过同义词引用转发目标。一些目标页面可能被多个动作类引用,例如登录页面,因此可以在全局转发定义部分把逻辑目标页面映射到物理目标页面,避免把这部分信息加入到动作定义部分。

  4. Actions定义了Struts应用体系的请求分配信息,它们是核心分配器的补充定义,负责处理各种具体的请求类型。
 
===================================================================================
一个简单的应用

  基于Struts的Web应用和普通Web应用有着许多同样的要求,但Struts应用也有自己特殊的需求。一个可部署的Web应用应该可组织和构成一个WAR文件。WAR文件是带有图2所示目录结构的JAR包。对于Struts Web应用来说,Web-INF目录下还要加上一些额外的文件,例如struts-confg.xml文件和标记库描述器(TLD)文件。注意:应用的资源应该放入应用的类路径下,也就是Web-INF/lib目录或Web-INF/class目录下的JAR包内。对于大多数简单的Struts页面,我们只用到Struts标记库,而按照MVC的术语就不需要涉及Model和Controller部分,只涉及View。请看图3所示的主页例子。虽然这个页面没有表单,但Struts仍能够在设计这类页面时提供帮助。

  

  图2 Struts应用的目录结构

  

  图3 一个简单的View

  要管理会话,最简单的途径是使用Cookie。会话标识符被传递到客户端之后,客户端把它保存到Cookie,以后的每次请求就把Cookie发送到服务器。然而,和其他的许多Web解决方案一样,Cookie方案也不是万能的,因为一些用户可能不信任Cookie,关闭浏览器的Cookie支持。由于这种情况,URL改写技术就出现了。使用URL改写技术时,整个网站的所有URL后面都将加上会话标识符。虽然这个方案不像采用Cookie方案那样简单、稳固,但它确实行得通。URL改写技术的不稳固是有两方面的原因。首先,和Cookie不同,URL没有过期时间,如果一个带有会话标识的URL被截取后又重新在以后的访问中使用,那么这种URL不会很有用,因为会话一般在一定的时间后会被作废。其次,如果有一个URL链接的后面没有带上会话标识符,整个链都会中断,客户程序无法再次获取会话标识符,除非它备份了带有会话标识符的URL访问历史。

Servlet能够只通过一次方法调用完成URL改写。从技术上讲,JSP也一样能够办到这一点,但一个好的JSP页面应该不包含Java代码,或包含尽量少的Java代码。为此,Struts提供了一个链接标记。本例使用了该标记来维持客户端和服务器之间的会话状态信息。

  绑定View、Model和Controller

  前面的简单页面不需要Struts分配器,因为它只有简单的链接。图4显示了一个比较复杂的“类别”页面。它列举出了数据库中的类别条目,并将这些条目分别链接到对应的编辑页面。为显示这个页面,我们就要用到Struts的ActionServlet分配机制。

  

  图4 类别页面

  在web.xml文件中,放入一项表示所有以“.do”结尾的URL请求必须发送给Struts分配器的声明。这里的分配器可以是org.apache.struts.action.ActionServlet或其扩展类。Struts分配器在启动时读取struts-config.xml文件,并构造出一个动作映射图。本例指定了一个名为ShowCategories的动作类,来处理“ShowCategories”动作。可以看出,Struts应用的基本工作模式是:主分配器调用一个动作分配器,动作分配器确定或构造出Model部分(一个JavaBean或其它Java对象),并把它提供给View(通常是一个JSP页面)。

  本例使用Bean的情况稍微有点复杂,它有多个数据项,因此我们不是使用单个提供数据的Bean,而是要生成一组Bean。遗憾的是,JSP页面以HTML为基础,HTML没有提供循环或其他控制逻辑。不过,Struts的logic:iterate允许对数组进行迭代操作,如下面的代码片断所示:

<table>
<logic:iterate id="category"
type="com.strutsdemo.Category"
name="<%= Constants.CATALOG_CATEGORIES %>"
scope="application">
<tr>
<td>
<html:link page="/editCategory.do"
 name="category"
 property="mapping">
 编辑
</html:link>
<html:link page="/removeCategory.do"
 name="category"
 property="mapping">
 删除
</html:link>
<bean:write name="category"
 property="category"/>
</td>
</tr>
</logic:iterate>
</table>


  上面讨论了Model 2体系的Model和View部分,下面来看看Controller部分。Struts体系有一个主控制器,即ActionServlet。ActionServlet负责选择和调用合适的动作控制器—即org.apache.struts.action.Action的扩展类。动作控制器实现了process()方法。process()方法分析从URL请求传入的每一个参数,执行必要的业务逻辑,并返回一个指定了调用链中下一个链接的动作(通常是View)。在本例中,我们想要从数据库提取数据,创建管理这些数据的JavaBean,把多个JavaBean整理成一个数组,再把数组保存到请求的上下文,从而使得作为View的JSP页面能够方便地进行页面布局。

===================================================================================

为保证上述操作顺利进行,在struts-config.xml文件中加入声明,指定由哪一个动作处理器来处理指定的动作:

<action path="/showCategories"
   scope="request"
   type="com.strutsdemo.ShowCategoriesAction"
   unknown="false"
   validate="false">
   <forward name="viewCategories" path="/	
   ShowCategories.jsp"/>
</action>


  表单处理

  表单处理过程充分体现出Struts的优势。在Web应用中,大部分复杂的HTML处理任务都涉及到表单。表单编辑过程具有类似图5所示的请求或应答结构。更新操作的过程与创建操作的过程相似,但对于更新操作来说,“创建Model”这一步骤变成“装入Model”,而“保存Model”变成了“更新Model”。请注意Web应用的特点:操作过程随时可能中止,这既可能是因为用户通过显式的动作取消了当前的操作,也可能是因为用户没有提交表单,例如用户跳转到了一个不是用来处理当前表单的URL。

  

  图5 表单处理流程

  表单编辑过程分三个阶段:这里分别称之为准备(Preparation)、表现(Presentation)和存储(Preservation)。准备和存储阶段都属于Struts动作,而表现阶段主要是客户端的活动。表1显示了该过程中涉及的各种部件:

  表单Bean(Form Bean)是一种特殊的JavaBean类型,它简化了表单处理。Form Bean从org.apache.struts. action.ActionForm类扩展而来。Form Bean有几个有用的特点,例如,通过reset()方法可以把Bean的属性设置成默认值,通过validate()方法让Bean验证属性的合法性。更重要的是,ActionServlet确保Form Bean被创建且可供它的动作方法调用。HTML标记库还能够确保Form Bean被正确地初始化并从Form View获取数据。

  Form Bean应当属于Model部分,然而,由于它有validate()方法,因此从某些特征来看它更接近分配器。不过,不必太在乎这些概念上的问题。Model 2并不完全等同于MVC,而且一些人已经在责难MVC不外乎是几种简单设计模式的混合物。不管怎样,从应用实践的角度来讲,系统的稳定性远比概念的严格性更重要。在本例中,这个问题更加富有代表性,因为我们把持久化机制也包装到Form Bean里面。从技术上看,Bean数据的持久化副本就是一个View,因此,从这个意义上来讲,我们现在有了一个结合了分配器和View特点的Model。这种设计方式看起来似乎否定了引入Struts之类框架的理由,但实际上,这种设计方式两方面的特点弥补了许多遗憾。

  首先,由于验证代码和SQL代码在很大程度上依赖于Form Bean拥有的属性,所以把它们作为一个单元管理会带来很大的方便。由于这里只对Form Bean的属性感兴趣,“重量级”的分配器和View部件都得到了有效的隔离。其次,Form Bean与HTML标记库一起使用时,Form Bean可以包含其他对象。这些对象可以通过“.”符号应用。使用预定义的Java对象时,“.”引用方式能够带来很大的方便,因为Java不支持多重继承。“.”引用方式避免了手工编写大量get/set代码的繁杂工作。

  当内部对象是EJB时,“.”引用方式带来的方便更加突出,因为在JSP页面中引用EJB时,EJB往往显得很“笨重”。如果EJB嵌入到了Form Bean里面,许多这方面的遗憾就不再存在。更重要的是,它分离了Controller和Model,而且View持久化也简缩到了最简单的程度,因为EJB容器可以处理所有持久化方面的细节。这样,Form Bean就几乎成了一个纯粹的分配器,一切都变得整洁和清晰。

===================================================================================



  如果EJB有大量的属性,而且按照ActionServlet通常对Form Bean所做的那样,按照每个属性分别更新的方式进行更新,就会出现大量的RMI调用开销。对于要求较高的应用,更好的选择是利用EJB 2.0本地接口,或者在EJB之前加上一个传统的JavaBean(通常是会话EJB),并把该Bean传递给实体Bean的UpdateAllProperties()业务方法。后面这种方案允许在单个RMI调用中完成所有的更新操作。
阅读全文
0 0

相关文章推荐

img
取 消
img