UML软件工程组织

一个掌握Struts企业级Web开发框架的实例中
作者:俞良松 选择自 开放系统世界
为保证上述操作顺利进行,在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调用中完成所有的更新操作。

准备阶段

  一次典型的编辑会话要求有一个动作处理器准备View,即一个作为View的JSP页面,还要求有第二个动作处理器存储更新后的View。当然,存储操作之后会有第二个属于View的页面被显示,例如一个“数据已经更新,点击此处继续”的页面(参见表1)。

   表1:基于Form Bean的编辑过程要用到的部件

部件 说明
CatalogForm Form Bean
EditCategoryAction 准备阶段
EditCategory.jsp 编辑
SaveCategoryAction 存储阶段
EditDone.jsp 确认数据已经保存
EditFailed.jsp “数据没有保存”错误


  下面的代码片断显示了如何在struts-config.xml文件中配置准备阶段:


<action path="/editCategory"
   scope="request"
   name="catForm"
   type="com.strutsdemo.EditCategoryAction"
   unknown="false"
   validate="false">
   <forward name="success"
   path="/EditCategory.jsp"/>
</action>


  在准备阶段,容器尝试从Session或Request找出指定的Form Bean,这是因为在动作中指定了“name=...”。ActionServlet在struts-config.xml文件的 区域寻找Form Bean的别名,利用Form Bean的别名寻找对应的Java类。如果用户的请求带有参数,其名字匹配Form Bean属性名字的参数将被设置为属性值。Struts扩展了“属性名字”的含义,使得访问Form Bean内嵌对象的属性成为可能。本文的例子也用到了Struts的这一优点。

  准备好Form Bean之后,ActionServlet接着调用动作的process()方法,Form Bean作为参数之一传入process()方法。在这里,我们对Form Bean的属性作最后的调整,调用业务方法,委派作为View的EditCategory,从而生成一个以Form Bean中合适数据为基础的HTML页面。这个页面被传递给客户端,接下来就进入了“表现”阶段。

  表现阶段

  这一阶段用户编辑表单并提交。如果服务器端的应用认为用户提交的内容存在问题,它把表单再次显示给用户,加上适当的提示信息;重复该过程,直至用户提交了合法的表单,或取消了表单处理过程。编辑过程的中止可能是由于用户跳转到了其他页面,或者启动了一个取消动作(例如点击了一个由html:cancel标记定义的按钮)。虽然在理论上,View的验证和再次显示操作应该属于表现阶段,但在Struts应用中,这部分功能在存储阶段实现最方便。

  存储阶段

  准备阶段创建了一个带有“name=”属性定义的动作CatForm,存储阶段要加入另外两个属性,即:“validate=‘true’”和“input=”属性。


<action path="/saveCategory"
   scope="request"
   name="catForm"
   type="com.strutsdemo.SaveCategoryAction"
   unknown="false"
   input="/EditCategory1.jsp"
      validate="true">
   <forward name="success"
      path="/CategoryUpdated.jsp"/>
</action>


  设置了“validate=‘true’”属性选项之后,服务器端就会增加一个处理步骤。重新用来自View的数据构造出Form Bean,或更新From Bean的时候,Form Bean的validate()方法会被调用。validate()方法执行必要的合法性验证操作。如果用户的输入数据中存在错误,validate()方法就创建一个或多个ActionError对象。这些ActionError对象包含了错误信息源ID和表单输入域的名称。这些ActionError对象被收集和整理到一个ActionErrors对象,随后ActionErrors对象由validate()方法返回。如果用户输入的数据不包含错误,validate()返回null。

  由于指定了“input=”属性,一旦出现了错误,动作会被忽略,而“input=”指定的View被显示。这个View既包含Form Bean,也包含当前出现的错误对象集合。一般地,这个输入页面就是原来执行编辑功能的JSP页面。

  大多数Struts的html标记有对应的HTML标记,但Struts有一个HTML没有的标记,即 标记。要中止表单编辑过程,用户既可以手工输入URL,也可以点击不指向存储动作处理器的链接。因此,用 标记定义的“取消”按钮,不是取消编辑操作的唯一方法。

  假设validate()方法没有发现任何错误,且用户没有点击“取消”按钮,存储动作的process()方法将被调用。在本例的process()方法中,我们调用了Form Bean的save()方法把数据写入持久性存储设备,然后根据写入操作是否成功,显示“存储操作成功”或“存储操作失败”的View。

 

 

版权所有:UML软件工程组织