|
|
| 克服Struts开发障碍(一) |
作者:Michael Coen,Amarnath Nanduri
文章来源:赛迪网
访问次数:4601次
加入时间:2004年02月14日
|
|
目录: 一、摘要 二、挑两个,早上呼我 三、现在,我将表格放在哪里? 四、再说一次,我们是怎样关联的?
一、摘要
建立和维护企业应用程序非常困难。而为这些应用设计出上乘的、易于维护的用户界面则是所有工作中最让人畏惧的任务。来自Apache Jakarta 项目的Struts框架为J2EE(Java2平台企业版)带来了Model 2 结构。在本文中,两位作者讨论了开发者在使用Struts 的过程中所遇到的问题,以及简化这些问题的相应方法。
除非你过去几年内潜居于石洞之中,否则你不可能没听说过Struts framework。Struts是由Apache软件基金会最初发起的开源,主要是为了促进Web应用演示层内的模型-视图-控制器(MVC)设计范例。truts 提供了使用Service的MVC模式给Worker 模式。一个设计优秀的结构总是力争耦合宽松、结合性高。Struts为在多捆绑的企业Web应用的演示层实现这个目标提供了一个机制。 实现企业应用结构所面对的最让人望而生畏的任务之一就是演示层的创建和维护。用户期望得到非常功能化的、坚固的、和优雅的灰土用户界面。因此,演示层的代码库使得应用层超负荷运行。另外,不同的显示平台如无线电话和PDAs 的出现使得原本复杂的状况更加复杂的多。
各种不同的书和文章已经讲述了Struts的内部工作原理并且教我们如何使用这个框架。本文详细阐述了使用Struts 的Web应用开发者所遇到的问题,以及如何解决这些问题。下列方法中有许多可以应用到不同的MVC框架中如即将上市的JavaServer Faces 规范。 Craig R. McClanahan,Struts的创始人之一,造就了这个规范。
本讨论的主题包括:在使用Struts框架,用BEA WebLogic Server建立J2EE(Java2平台企业版)应用的过程中出现最多问题的所有区域。我们将讨论下列专题: ?创建/维护 struts-config.xml ?表格/会话期管理 ?Struts 映射和用户界面的关系 ?管理Back按钮 ?用户认证 ?用户界面控制流程 ? 异常处理 ?测试
二、挑两个,早上呼我
Struts框架毫无疑问,减轻了企业应用程序的用户界面的开发和维护。但是,即使只是在一个简单的应用中使用了Struts ,开发者也会迅速的认识到struts-config.xml这个恶魔。 这个文件很有可能迅速变得难于处理。在建立企业应用时,struts-config.xml 能够多出 500个动作映射,使得自身变得真正地难于管理。
我们推荐两个工具来帮助治理这个头疼的问题。首先,使用来自Alien-Factory 的Microsoft Visio 和StrutsGUI 文档化你的用户界面流程。StrutsGUI是一个Visio 模版,它对使用Struts 术语描述用户流程图有帮助。在Struts 模版内有一个隐藏的功能:只要右键点击该项,选择Edit Title Properties,然后选择Tools项,你就能够在该图的基础上生成struts-config.xml 文件。例如,图1中显示的简单应用生成了如下列代码所示的struts-config.xml :
Figure 1. StrutsGUI model. Click on thumbnail to view full-size image.
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!-- Struts Config XML - Sample Struts App --> <!-- ===================================== -->
<!-- AutoGenerated from : c:devjavaworldappsample.vsd --> <!-- AutoGenerated on : 02-18-2003 23:05:47 --> <!-- AutoGenerated by : Struts GUI v2.11 (c)2002 Alien-Factory --> <!-- : See 'http://www.alien-factory.co.uk' for details -->
<!-- GET YOUR STICKY FINGERS OFF! i.e. Do not edit. -->
<!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>
<!-- ====================== Form Bean Definitions =================== --> <form-beans> </form-beans>
<!-- ================= Global Forward Definitions =================== --> <global-forwards> </global-forwards>
<!-- ======================= Action Definitions ===================== --> <action-mappings> <action path="/Login" type="com.agilquest.onboard.presentation.actions.LoginAction"> <description>Authenticates and authorizes a user.</description> </action> </action-mappings> </struts-config>
为了使你的用户界面流程图更加复杂,我们推荐在使用StrutsGUI 时增加一个步骤。在你的StrutsGUI Visio 文档内,你可以轻易的将每个JSP (JavaServer Pages)页链接到它在应用中的实际屏幕快照。创建这个界面不但确实有助于应用程序的文档化,更重要的是,它成为了培训新的从事用户界面设计的开发者的一件极好的工具。
另一个帮助管理Struts应用的工具就是由James Holmes 发明的Struts Console。本质上,这个工具提供了一组设备,这些设备使你能够得到与StrutsGUI 相同的终点,但是它们在途径和长度上有区别。这两个工具都执行良好,其中任何一个都可以增强基于Struts的企业应用的可维护性。
三、现在,我将表格放在哪里?
ActionForm 会话期管理有点棘手。ActionForm的周期怎样?它在请求周期内或者会话期周期内吗?为了得到它所代表的功能周期,方案之一就是将ActionForm置于会话期之内。在这种情况下,你通常怎样维护这些ActionForm 对象呢?谁知道不再需要他们时删除他们要承担什么责任呢?典型的情况是:用户通过菜单从一个功能转为使用另一个功能。在这种情况下,原来的ActionForm 对象就应该从会话期删除,并且创建新的ActionForm 对象。这时还应该出现一个集中的Action类,MenuAction,它只处理菜单切换。这个Action 类从会话期删除多余的ActionForm 对象。然后将用户前进到创建新ActionForm 对象所在的新页面。
在这种情况下,站在用户的立场上或者基于用户权限,我们应该如何显示不同的菜单项呢?这个菜单也应该国际化,并且它修改时应该以用户权限为基础;也就是说,如果许可修改了,菜单也要相应的修改。有一种方法可以持续用户的权限。当用户注册时, MenuFactory根据这些权限创建菜单,为了增加安全性, MenuAction类在允许用户进行到他所选择的功能之前需要认证用户。命名struts-config.xml 中的ActionForm 对象的首要规则是对象名以Form 结束,从而简化了会话期内这些表格的维护。例如: ReservationForm, SearchUserForm, BankAccountForm, UserProfileForm,等等。
下列代码描述了一个具有Action 映射的普通菜单切换动作,它进一步阐明了ActionForm(s)管理:
public class MenuAction {
public ActionForward perform(ActionMapping _mapping, ActionForm _form, HttpServletRequest _request, HttpServletResponse _response) throws IOException, ServletException {
// Check end-user permissions whether allowed into the requested // functionality checkIfUserAllowedToProceed(_mapping, _form, _request, _response);
// Clean up the session object (this logic is in its own method) String formName = null;
HttpSession session = _request.getSession(); Enumeration e = session.getAttributeNames();
while(e.hasMoreElements()) { formName = (String)e.nextElement();
if (formName.endsWith("Form")){ session.removeAttribute(formName); } }
// Now find out which functionality the end-user wants to go to String forwardStr = _request.getParameter("nextFunctionality");
if (forwardStr != null && forwardStr.trim().length() > 0){ return _mapping.findForward(forwardStr); } else { return _mapping.findForward("index"); } } }
下列Action映射就是一个阐述如何以菜单选择为基础实现动作的例子:
<!-- A generic menu action that forwards the user from one functionality to another functionality (after checking permissions) --> <action path="/menuAction" type="x.y.z.MenuAction" input="/menu.jsp"> <forward name="create_reservation" path="/actionResv.do"/> <forward name="index" path="/menu.jsp"/> <forward name="add_person" path="/actionPerson.do"/> <forward name="logout" path="/actionLogout.do"/> </action>
例子和映射都是可以自我解释的。
四、再说一次,我们是怎样关联的?
任何JSP页的许多输入点与许多现有点之间都有一个关系,这取决于页面本身的复杂程度。认识到这些关系对于理解和维护用户界面是至关重要的。我们已经将JSP页和Action 类之间的关系定义为: ?1:1 关系 ?1:N 关系 ?N:N关系
1:1 关系 在1:1 关系中,用户通过Action类从一个JSP页切换到另一个页面;这就使得JSP页和Action 之间容易形成一个紧密的耦合。唯一的额外开销就是struts-config.xml 中有一个Action映射。这个在struts-config.xml 中只有一个Action 映射的简单Action 可用于从一个页面切换到另一个页面。直接通过一个JSP页访问另一个JSP页是不太现实的;他不能够检查转向目标JSP页的用户权限(如果可行的话)。他还导致了维护方面的问题。为了避免这些问题,可以总是通过Action 类从一个JSP页转向另一个JSP页:
<!-- A generic action that forwards request from one JSP page to another JSP page --> <action path="/forwardAction" type="x.y.z.One2OneAction" input="/test1.jsp"> <forward name="continue" path="/test2.jsp"/> </action>
1:N关系 稍微复杂一点的关系就是JSP页有多个现有点但是只有一个输入点,也叫做1:N关系。在这种情况下,总是使用一个单一 Action类分支到不同的目标。这就保证了Action 在将用户推进到目标之前能够检查不同的情况或者权限。唯一的额外开销就是在struts-config.xml 中有一个Action 映射。这也推动了JSP页与Action 映射之间的1:1 映射。下面的Action映射标出了一个映射,它有一个单一输入点和多个前推,多个前推代表着多个现有点:
<!-- A generic action that forwards request from one JSP page to different branches depending on the selected hyperlink, by the end-user --> <action path="/branchAction" type="x.y.z.One2NAction" input="/test1.jsp"> <forward name="target1" path="/test2.jps"/> <forward name="target2" path="/test3.jsp"/> <forward name="target3" path="/someAction.do"/> </action>
N:N 关系 最复杂的关系,即N:N关系,指的是JSP页或者Action 类有多个输入点和多个现有点。N:N 关系是频繁出现在企业应用程序中的一个有趣和复杂的部分。N:N关系最初应用在不同的JSP页访问一个公共JSP页或者一个公共Action类的情况下。假设用户进入的JSP页是一个网络中心(特别是这个JSP页由不同的JSP页都可到达),但是用户又想返回或者取消这个流程;那么开发者就处于进退两难的局面:不知道怎样将用户送往正确的页面。
另一个场景是:Action类接合到数据库(通过不同的功能函数或者JSP页),并且出现错误。以用户原来所在的位置为基础,将用户发送回原来所在的位置或者适当前推,这需要仔细推敲。struts-config.xml映射证明是没有帮助的,因为输入域是一个确定的JSP页或者 Action 类。我们创建的结构应该足够灵活,这样开发者不必在struts-config.xml 折腾就能够轻而易举的修改流程逻辑。这就是N:N关系所要解决的问题。通过实现一个能够灵活发送用户到目标所在地或者目的文件的界面,这个值修改起来就比较容易。下面的Action映射给出的映射有多个输入点和多个前推,这些前推代表多个现有点:
public class N2NAction {
public ActionForward perform(ActionMapping _mapping, ActionForm _form, HttpServletRequest _request, HttpServletResponse _response) throws IOException, ServletException {
N2NInterface if = (N2NInterface)_form;
//Execute some business functionality here try{ //Business logic successful? } catch(Exception e){
//Indicates failure return _mapping.findForward(if.getSource()); }
//Indicates success return _mapping.findForward(if.getDestination());
}
}
<!-- A generic action that forwards request from one JSP page to another JSP page --> <action path="/sourceAndDestinationAction" type="x.y.z.N2NAction" input="/test1.JSP"> <forward name="source1" path="/source1.JSP"/> <forward name="source2" path="/source2.JSP"/> <forward name="source3" path="/someAction.do"/> <forward name="destination1" path="/destination1.JSP"/> <forward name="destination2" path="/destination1.JSP"/> <forward name="destination3" path="/destination2.JSP"/> </action>
A hyerplink can be something like <a href="sourceAndDestinationAction.do? source=source1&destination=destination1">click me</a> <a href="sourceAndDestinationAction.do? source=source2&destination=destination2">click me too</a>
所有的ActionForms 默认地都必须所有这三种关系(通常通过界面)。使用普通的Action 类,你可以自由地在用户界面流程中移动。
|
|
|