Jakarta Struts学习之应用实践

        我们在这篇文章将会一步一步的讲解Struts的应用,以这样的形式打开Struts一道道神秘的大门,通过这样的过程,相信也能激起你在应用开发中如何应用Struts的灵感。如果你对Struts的一些术语不是很清楚的话,可以参考本系列前一篇对Struts作大体介绍的文章。

再次重复一遍,本文需要读者有如下几方面的知识和经验:JSP,Servlets,自定义标签库(Custom Tag libraries)和XML。此外,在本文中,我还会用到Jakarta项目组其他一些好东东,比如Tomcathttp://jakarta.apache.org/tomcat/index.html(实现Java Servlet和JSP官方标准的Servlet容器,通俗的讲就是一个JSP的Web Server啦)和Anthttp://jakarta.apache.org/ant/index.html(基于Java的自动编译发布工具,这可是好东东啊)。

作为一名一直使用前沿技术开发了诸多应用的技术人员,我一直坚信掌握新技术,理解该技术开发的逻辑是至关重要的。但这往往就是陷住我们学习步伐的泥潭,正因如此,我打算将利用Struts开发的一套完整流程作为我们教学的案例。该流程的这个案例可谓“麻雀虽小、五脏据全”,你完全可以将这个流程应用到你手头那些复杂庞大的项目中,至少在我们的大项目中应用这个流程效果不错。

有开发复杂商业应用的开发人员都知道,客户的需求总是在不停变幻,所以如果有一套规范的开发流程来遵循,当客户提出新的需求时,我们至少可以明确哪些“无理”需求其实是合理可行的。好,接下里我将在我的这个例子中向各位展示和应用整个流程。

本文中的示例代码是StrutsSample应用中的一部分,包括build.xml的完整代码可以到此处 http://www.onjava.com/onjava/2001/10/31/examples/StrutsPartII.jar下载。

Struts开发过程

从Struts发布的版本号可以看出,Struts是个新玩意,她有好几个部分组成,明智的你如果搞清楚了何时该开发完成合适的部分,那将会更好的利用我们的开发时间。从我所开发的几个利用Struts应用中,我大致总结出如下这个比较有效的开发步骤:

1,明确应用需求;

2,由用户输入和获取数据的角度出发,明确和设计出每一个用户界面;

3,确定用户界面的进入路径;

4,由应用逻辑信息确定动作映射表(ActionMapping);

5,由设计完成的用户界面开发其所用到的类和应用函数;

6,由用户界面中的数据信息开发ActionForm和相应的数据校验方法;

7,ActionMapping中将会被调用相应的Action或转到相应的JSP页面,这一步我们先开发这些Action;

8,开发商业应用逻辑,就是相应的JavaBean、EJB或其他东东;

9,开发由ActionMapping定义的系统工作流程完成对应的JSP页面;

10,完成系统配置文件:struts-config.xml和web.xml;

11,编译/测试/发布。

明确应用需求

开发任何应用系统的第一步就是收集用户需求信息。不管一个用户逻辑初看上去多么合理,但总有可能在开发时才发现它比看上去要难得多。所以,建议拟一份明确的用户需求列表,这不只是出于开发的目的,还能通过该表分析用户需求以确定哪些地方可能需要花更多的精力。

在我们这个StrutsSample项目中,应用需求就是:

作为一个展示Struts框架应用的完整例子,本示例完成的功能是用户登录。目的只为明确Struts的应用,本示例将不会涉及到一般复杂应用系统中可能应用的安全、数据库、EJB开发等等相关技术。

设计用户界面

这个应用中,包括如下三个用户界面:

1)登录界面,用于用户名和密码输入;

2)当登录用户为合法用户时的欢迎界面;

3)当登录失败时的错误提示界面。

确定用户界面的进入路径

1)登录界面作为这个应用的默认页面;

2)欢迎界面只有当成功登录后才能进入;

3)任何可能发生错误的页面能可以进入错误提示界面;

由应用逻辑信息确定ActionMapping ActionMapping为整个应用确定的“线路图”,在配置文件struts-config.xml对ActionMapping进行定义,通过转发请求(forward)来理顺应用的处理流程,确定应用中每个用户请求对应的动作。 通常我们在开发过程中就逐步确定了ActionMapping所需的信息,开发代码的过程就是在由草稿开始一步步完善struts-config.xml的过程。当Action类处理完用户请求后,其返回的的forward就是在ActionMapping中定义的一个。一个Action返回的forward完全有多种可能,尽管一个Action一般只定义其相关的几个forward。那么,如果有多个Action都可能返回的同一个forward,那么就可以将其定义为全局转发(global forward)。这类似于C中的头文件中全局变量,如果在struts-config.xml描述信息中,某一个forward并不是在当前Action描述中定义的而是全局定义的,那么这个全局的将起作用,同样,一个Action中当前定义的forward将覆盖全局定义。在我们所给的这个简单实例中,我们定义了全局forward――“error”,当某Action返回的forward是“error”这个映射,那么Errorpage.jsp页面将会显示给用户,尽管当前Action并没有对其定义。 我们继续不断的开发,项目日渐完善,项目相关的配置文件也会越来越详细。在下面的例子中,我们将以StrutsSample中用到的struts-confug.xml文件为例,学习global forward和一个Action中相关映射的定义。下面定义了一个名为“login”的Action,其为com.oreilly.actions.LoginAction的实例,当Action处理用户登录成功后将一个名为"success"的forward返回,用户也就会看到Welcome.jsp页面,如果登录失败,Action将返回对应的forward以再显示Login.jsp给用户,而如果处理过程中发生其他错误,Action将返回全局定义的forward――“error”,用户也就会看到错误提示页面Errorpage.jsp。

      
      
       
       <!-- ========== Global Forward 定义 --> <global-forwards> <forward name="login" path="/Login.jsp"/> <forward name="error" path="/Errorpage.jsp"/> </global-forwards> <!-- ========== Action Mapping 定义 --> <action-mappings> <!-- <action>元素的相关属性 --> <!--
      
      


以下只列出常用属性,其他请参考org.apache.struts.action.ActionMapping的相关文档

path - 当前Action对应的用户请求URI路径

type - 实现当前Action的Java class的完整名字

name - 当前Action中用到的ActionForm的名字,其具体信息在配置文件其他地方另有详细定义

unknown - 如果将该属性设置为true,那么就是声明这个Action将处理整个应用中所有未找到相应处理Action的请求,当然,一个应用系统中也只会有一个Action的unknown属性可以设为true

scope - Action中所用到的ActionForm的生存期,可以为“request”或“session”,随着生存期的设置,该Action也会在相应的时间被创建

input - 该Action中相关ActionForm获取用户输入的输入页面,当将ActionForm设为自动验证输入数据,发现不合法数据返回错误时,将返回该页面

validate - 如果本属性为true则在Action动作之前其对应的ActionForm的validate方法会自动被调用,一般用以验证用户输入的数据

forward 元素 - 定义当前Action相关的ActionForward

      
      
       
       --> <!-- =================== --> <!-- O'Reilly Struts Sample Main Actions --> <!-- =================== --> <action path="/login" type="com.oreilly.actions.LoginAction" name="loginForm" scope="request" input="/Login.jsp"> <forward name="success" path="/Welcome.jsp"/> <forward name="failure" path="/Login.jsp"/> </action> </action-mappings>
      
      


在前一篇文章中,我们曾说过,struts-config.xml就是MVC模式的的Controller。在确定struts-config.xml中的配置信息时,应该多花些时间精力在上面,以保证每一个Action定义及其相关定义是符合应用的需求的。如果在项目开始没有详细的设计其定义,当将所有代码和配置集成到一起的时候,我们将不可避免的将各部分的代码和配置完全重新组织一遍。 我们当前的例子StrusSample因为只是处理用户登录,所以只需要一个Action。一个应用系统中所要用到的Action的多少完全依应用的大小而定。一旦整套Action的映射完全的定义出来后,我们就可以一个一个开发其具体实现的Action和ActionForm类,并逐渐将完成的部分一点一点集成起来。

由设计完成的用户界面开发其所用到的类和应用函数 所有ActionForm的实现类都是org.apache.struts.ActionForm的子类。一个ActionForm是与页面上的输入表单相关联的,而且ActionForm的实现还可以对用户输入数据的合法性进行验证。作为一个Java Bean,ActionForm有Set和Get方法,当一个页面中表单被提交时,系统将自动调用Set方法将数据放入ActionForm中,而Get方法将为在Action中操作这些数据所提供。一般来说,处理表单中的所有数据,并进行合法性验证都完全可以交由ActionForm来完成。在应用中,就我个人而言,倾向于将ActionForm和Action划分到不同的包中,因为当一个页面中要用到几对ActionFrom和Action时,都放在一个包内会混淆的。下面的代码,就是实例中登录页面用到的ActionForm的代码。

      
      
       
       /* * LoginForm.java */ package com.oreilly.forms; import javax.servlet.http.HttpServletRequest; import org.apache.struts.action.ActionError; import org.apache.struts.action.ActionErrors; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionMapping; /** * 验证用户要用到的两个数据 * * username - 登录用户名 * password - 用户密码 * */ public final class LoginForm extends ActionForm { private String userName = null; private String password = null; /** * userName的Get方法 * @return String */ public String getUserName() { return (userName); } /** * userName的Set方法 * @param userName */ public void setUserName(String newUserName) { userName = newUserName; } /** * password的Get方法 * @return String */ public String getPassword() { return (password); } /** * password的Set方法 * @param password */ public void setPassword(String newPassword) { password = newPassword; } /** * 重置所有数据 * * @param mapping 当前的ActionMapping * @param request 当前Server正在处理的HttpServletRequest */ public void reset(ActionMapping mapping, HttpServletRequest request) { userName = null; password = null; } /** * 验证当前HTTP请求提交上来的数据 * 如果数据验证发现不合法数据,将返回一个封装 * 所有验证错误的ActionErrors对象 * 如果数据验证通过,该方法返回null或者一个 * 没有封装任何验证错误的ActionErrors对象 * * @param mapping 当前的ActionMapping * @param request 当前Server正在处理的HttpServletRequest */ public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { ActionErrors errors = new ActionErrors(); // 当前ActionForm中,只需要检查用户输入的用户名数据 if( userName == null || userName.length()==0 ){ errors.add("userName",new ActionError("error.userName.required")); } return (errors); } }
      
      


以上的代码,只有两点和一般的Java Bean有所不同。其一是reset方法,方法中设置的值将在表单被reset时反应到其对应的表单项上,即将表单项的数据恢复到默认值。其二是validate方法,是用来验证用户在表单中所输入数据的方法。在当前这个例子中,我们只验证用户输入的用户名。因为一个用户名其对应的密码可能为空,所以我们的逻辑就是验证时不去检查密码。验证用户名,当发现输入的用户名为空时,方法就会产生一个错误对象(ActionError)。

在Struts中用ActionErrors来装载多个错误,从ActionErrors结尾的那个“s”就可以知道她是一个ActionError对象的集合。在验证用户输入时,可以验证完表单中所有数据后,再将可能发现的多个错误通过ActionErrors返回给用户,这样的逻辑应该是想当然的啦,不可能用户有五个不同的输入错误,却要分五次提示,让用户修改提交五遍吧,呵呵。

同时,要知道在我们这个例子中,我们将错误信息提示给用户是通过ApplicationResource.properties文件。这个文件在Tomcat启动时通过web.xml中的定义为这个应用所使用。通常每一个应用都在其WEB-INF目录下都有web.xml文件来描述系统,而关于部署应用时具体的结构信息,请参考 Tomcathttp://jakarta.apache.org/tomcat/index.html等Server相关的用户手册。

ApplicationResource.properties文件中可以定义应用中所要用到的提示信息的字符串,字符串都通过一个键值来唯一确定其位置。在我们这个例子中,键值error.userName.required所对应的字符串信息是“A username is required”,在给用户显示错误信息时,也就通过键值确定的错误提示显示该字符串。通过这样的机制,为我们在系统中实现多语言提供了便利,将该文件中的这些字符串翻译成相应的语言,我们的系统就可以实现西班牙语、德语、法语和汉语等等语言的版本了。

下面这些ApplicationResource.properties中的信息对于我们这个简单的示例中已经够用了:

      
      
       
       login.title=Login Struts Sample error.userName.required=A username is required error.login.authenticate=Invalid username/password errors.footer=</ul><hr> errors.header=<h3><font color="red">Page Validation</font></h3>Please correct the following error(s) before contiuing:<ul> applicationResources=Cannot load application resources bundle {0}
      
      


页面的标题,按钮或其他什么需要文本提示的地方都可以通过这个文件来定义显示用字符串。我们将在该系列的最后一篇文章,也就是后续几个开发步骤中讲解如何通过Struts的标签从这个文件中获取显示用字符串。

待续
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值