本文主要讲解什么是Struts Framework,它的框架结构,组件结构,以及简单的配置讲解。
文章的包括了如下七大部分:
Framework的概念和体系简介
Struts的概念和体系结构
Struts的工作原理和组件
Struts配置文件简介
Struts高级特性
Struts标记库
一个简单的示例
一直以来我们都说Struts是一个Web Framework。那么让我么先来看看什么是Framework。
Framework概念并不是很新了,伴随着软件开发的发展,在多层的软件开发项目中,可重用、易扩展的,而且是经过良好测试的软件组件,越来越为人们所青睐。这意味着人们可以将充裕的时间用来分析、构建业务逻辑的应用上,而非繁杂的代码工程。于是人们将相同类型问题的解决途径进行抽象,抽取成一个应用框架。这也就是我们所说的Framework。
Framework的体系提供了一套明确机制,从而让开发人员很容易的扩展和控制整个Framework开发上的结构。 通常,Framework的结构中都有一个“命令和控制”组件("command and control" component)——Framework Factory and Manager。
Framework体系
通过基于请求响应(Request-Response)模式的应用Framework,基本上有如下几个表现逻辑结构组成。
控制器(Controller)——控制整个Framework中各个组件的协调工作。
业务逻辑层(Business Logic)——对Framwork本身来说,这里仅仅只是概念和几个提够服务的基础组件,真正的实现与客户的业务逻辑接轨,还需要开发人员在Framework上再次扩展。
数据逻辑层(Data Logic)——绝大应用系统都需要涉及到数据交互,这一层次主要包括了数据逻辑和数据访问接口。对于数据逻辑来说,如果你了解数据建模(Data Modeling)可能就很容易理解。
Struts有一组相互协作的类、Serlvet以及Jsp TagLib组成。基于Struts构架的web应用程序基本上符合JSP Model2的设计标准,可以说是MVC设计模式的一种变化类型。根据上面对framework的描述,我们很容易理解为什么说Struts是一个web framwork,而不仅仅是一些标记库的组合。但 Struts 也包含了丰富的标记库和独立于该框架工作的实用程序类。
Struts有其自己的控制器(Controller),同时整合了其他的一些技术去实现模型层(Model)和视图层(View)。在模型层,Struts可以很容易的与数据访问技术相结合,包括EJB,JDBC和Object Relation Bridge。在视图层,Struts能够与JSP, Velocity Templates,XSL等等这些表示层组件想结合。
既然struts叫做web framework,那么其肯定主要基于web层的应用系统开发。按照J2EE Architecture的标准,struts应当和jsp/servlet一样,存在于web container一层。
Struts与WebApp的关系
我们说struts framework是MVC 模式的体现,下面我们就从分别从模型、视图、控制来看看struts的体系结构(Architecture)。下图显示了struts framework的体系结构响应客户请求时候,各个部分工作的原理。
Struts体系结构
首先,Struts提供了Java类org. apache.struts.action.ActionForm,Java开发者将该类细分来创建表单bean。在运行时,该bean有两种用法:
― 当JSP准备相关的HTML,表单以进行显示时,JSP将访问该
bean(它保存要放入表单中的值)。那些值是从业务逻辑或者是从先前的用户输入来提供的。
― 当从Web浏览器中返回用户输入时,该bean将验证并保存该输入以供业务逻辑或(如果验证失败的话)后续重新显示使用。
其次,Struts提供了许多定制JSP标记,它们的使用简单,但是它们在隐藏信息方面功能强大。例如,除了bean名称和给定bean中每个段的名称之外,页面设计者不需要知道有关表单bean的更多信息。
Struts虽然不直接有助于模型开发。在Struts中,系统模型的状态主要由ActiomForm Bean和值对象体现。
在Struts framework中, Controller主要是ActionServlet,但是对于业务逻辑的操作则主要由Action、ActionMapping、ActionForward这几个组件协调完成(也许这几个组件,应该划分到模型中的业务逻辑一块)。其中,Action扮演了真正的控制逻辑的实现者,而ActionMapping和ActionForward则指定了不同业务逻辑或流程的运行方向。
整个struts大约有15包,近200个类所组成,而且数量还在不断的扩展。在此我们不能一一介绍,只能列举几个主要的简要的介绍一下。下表说明了目前struts api中基本的几个组件包,包括action,actions,config,util,taglib,validator。下图则显现了这几个组件包之间的关系。其中action是整个struts framework的核心
org.apache.struts.action |
基本上,控制整个struts framework的运行的核心类、组件都在这个包中,比如我们上面提到的控制器ActionServlet。已经Action,ActionForm,ActionMapping等等。struts1.1比1.0多了 DynaActionForm 类。增加了动态扩展生成FormBean功能 |
org.apache.struts.actions |
这个包是主要作用是提供客户的http请求和业务逻辑处理之间的特定适配器转换功能,而1.0版本中的部分动态增删FromBean的类,也在struts1.1中被Action包的DynaActionForm组件所取代 |
org.apache.struts.config |
提供对配置文件struts-config.xml元素的映射。这也是sturts1.1中新增的功能 |
org.apache.struts.util |
Strtuts为了更好支持web application的应用,体统了一个些常用服务的支持,比如Connection Pool和Message Source。详细信息请参考 http://jakarta.apache.org/struts/api/org/apache/struts/util/package-summary.html |
org.apache.struts.taglib |
这不是一个包,而是是一个客户标签类的集合。下面包括Bean Tags,HTML Tags,Logic Tags,Nested Tags,Template Tags这几个用于构建用户界面的标签类。 |
org.apache.struts.validator |
Struts1.1 framework中增加了validator framework,用于动态的配置from表单的验证。详细信息请参阅 http://home.earthlink.net/~dwinterfeldt/ |
Struts的基本组件关系图 |
对于Struts 如何控制、处理客户请求,让我们通过对struts的四个核心组件介绍来具体说明。这几个组件就是:ActionServlet。Action Classes,Action Mapping(此处包括ActionForward),ActionFrom Bean。
ActionServlet继承自javax.servlet.http.HttpServlet类,其在Struts framework中扮演的角色是中心控制器。它提供一个中心位置来处理全部的终端请求。控制器ActionServlet主要负责将HTTP的客户请求信息组装后,根据配置文件的指定描述,转发到适当的处理器。
按照Servelt的标准,所有得Servlet必须在web配置文件(web.xml)声明。同样,ActoinServlet必须在Web Application配置文件(web.xml)中描述,有关配置信息如下。
全部的请求URI以*.do的模式存在并映射到这个servlet,其配置如下:
一个该模式的请求URI符合如下格式:
http://www.my_site_name.com/mycontext/actionName.do
中心控制器为所有的表示层请求提供了一个集中的访问点。这个控制器提供的抽象概念减轻了开发者建立公共应用系统服务的困难,如管理视图、会话及表单数据。它也提供一个通用机制如错误及异常处理,导航,国际化,数据验证,数据转换等。
当用户向服务器端提交请求的时候,实际上信息是首先发送到控制器ActionServlet,一旦控制器获得了请求,其就会将请求信息传交给一些辅助类(help classes)处理。这些辅助类知道如何去处理与请求信息所对应的业务操作。在Struts中,这个辅助类就是org.apache.struts.action.Action。通常开发者需要自己继承Aciton类,从而实现自己的Action实例。
ActionServlet把全部提交的请求都被控制器委托到RequestProcessor对象。RequestProcessor使用struts-config.xml文件检查请求URI找到动作Action标示符。
一个Action 类的角色,就像客户请求动作和业务逻辑处理之间的一个适配器(Adaptor),其功能就是将请求与业务逻辑分开。这样的分离,使得客户请求和Action类之间可以有多个点对点的映射。而且Action类通常还提供了其它的辅助功能,比如:认证(authorization)、日志(logging)和数据验证(validation)。
public ActionForward execute(ActionMapping mapping,
ActionForm form,
javax.servlet.ServletRequest request,
javax.servlet.ServletResponse response)
throws java.io.IOException,javax.servlet.ServletException
|
当Controller收到客户的请求的时候,在将请求转移到一个Action实例时,如果这个实例不存在,控制器会首先创建,然后会调用这个Action实例的execute()方法。Struts Framework为应用系统中的每一个Action类只创建一个实例。因为所有的用户都使用这一个实例,所以你必须确定你的Action 类运行在一个多线程的环境中。下图显示了一个execute()方法如何被访问:
Action实例的execute()方法
注意,客户自己继承的Action子类,必须重写execute()方法,因为Action类在默认情况下是返回null的。
上面讲到了一个客户请求是如何被控制器转发和处理的,但是,控制器如何知道什么样的信息转发到什么样的Action类呢?这就需要一些与动作和请求信息相对应的映射配置说明。在struts 中,这些配置映射信息是存储在特定的XML文件(比如struts-config.xml)。
这些配置信息在系统启动的时候被读入内存,供struts framework在运行期间使用。在内存中,每一个
type="com.test.LogonAction" name="LogonForm" scope="request" input="logoncheck.jsp" validate="false">
|
type="com.test.LoginForm"/>
|
元素
return (mapping.findForward("welcome")); |
ActionForward对象是配置对象。这些配置对象拥有独一无二的标识以允许它们按照有意义的名称如“success”,“failure”等来检索。ActionForward对象封装了向前进的URL路径且被请求处理器用于识别目标视图。ActionForward对象建立自
type="packageName.EditCustomerProfileAction"
name="customerProfileForm" scope="request">
基于执行请求处理器的execute(…)方法的结果,当传递一个值匹配指定于
如果redirect=true, URL建立如/contextPath/path因为HttpServletResponse.sendRedirect(…)中解释URL采用”/”开头相对于servlet容器根目录。
如果redirect=false, URI建立如/path因为ServletContext.getRequestDisptacher(…)采用虚拟目录相关URL。
在此稍稍说一下有关global-forwards的概念。其在配置文件中描述了整个应用系统可以使用的ActionForward,而不是仅仅是一个特定的Action。
|
在上面讲解ActionServlet,Action Classes和Action Mapping的时候,我们都提到了ActionForm Bean的概念。一个应用系统的消息转移(或者说状态转移)的非持久性数据存储,是由ActionForm Bean的负责保持的。
ActionForm派生的对象用于保存请求对象的参数,因此它们和用户紧密联系。
一个ActionForm类被RequestProcessor建立。这是发生在已完成向前进到一个URL,该URL为映射到控制器servlet而不是JSP和相应的动作映射指定的表单属性的。在这个情况下,如果没有在指定的活动范围内找到,RequestProcessor将尝试寻找可能导致创建一个新ActionForm对象的表单bean。该ActionForm对象在指定的活动范围内被用
RequestProcessor将随后重新安排表单属性,用请求时参数填充表单,随即调用表单对象的validate(…)方法以履行服务器端用户输入验证。仅当ActionMapping对象中validate属性被设为true时,validate(…)方法被调用;这就是默认的行为。request.getParameterValues(parameterName)被用于得到一个String[]对象,它用来表单填充;验证的结果应该是一个ActionErrors对象,用org.apache.struts.taglib.html.ErrorsTag来显示验证错误给用户。ActionForm也可以被用于为当前用户保存即将被一个视图引用的中间模型状态。
当一个表单对象被RequestProcessor找到,它被传递到请求处理器的execute(…)方法。一个ActionForm对象也可以被请求处理器建立。表单对象建立目的是提供中间模型状态给使用请求范围JSP;这将确保对象不会在有效性过期后仍然存在。默认的,所有的表单都被保存为会话范围。会话中表单对象脱离有效性的存在可能导致浪费内存,同样的,请求处理器必须跟踪保存在会话中的表单对象的生命周期。一个好的捕获表单数据的实践是为横跨多用户交互的相关表单用一个单独的表单bean。表单bean也可以在反馈的时候用来储存能够被自定义标签改变的中间模型状态。在视图中标签用法避免结合Java代码,因此要成一个好的任务划分,web生产组主要处理标志,而应用开发组主要处理Java代码。标签因素退出访问中间模型状态的逻辑;当访问嵌套的对象或当通过聚集列举时这个逻辑可能很复杂。
注意:在struts1.1中,ActionForm的校验功能,逐渐被剥离出来(当然依然可以使用)。使用了validator framework对整个应用系统的表单数据验证进行统一管理。相信信息请参考:http://home.earthlink.net/~dwinterfeldt
在ActionForm的使用中,Struts提倡使用到值对象(Value Object)。这样将客户或开发人员,对数据状态与对象状态能够更加清晰的理解和使用。
对于每一个客户请求,Struts framework在处理ActionForm的时候,一般需要经历如下几个步骤:
(1)检查Action的映射,确定Action中已经配置了对ActionForm的映射
(2)根据name属性,查找form bean的配置信息
(3)检查Action的formbean的使用范围,确定在此范围下,是否已经有此form bean的实例。
(4)假如当前范围下,已经存在了此form bean的实例,而是对当前请求来说,是同一种类型的话,那么就重用。
(5)否则,就重新构建一个form bean的实例
(6)form bean的reset()方法备调用
(7)调用对应的setter方法,对状态属性赋值
(8)如果validatede的属性北设置为true,那么就调用form bean的validate()方法。
(9)如果validate()方法没有返回任何错误,控制器将ActionForm作为参数,传给Action实例的execute()方法并执行。
注意:直接从ActionFrom类继承的reset()和validate()方法,并不能实现什么处理功能,所以有必要自己重新覆盖。
Struts framework本身提供了很多可扩展的组件或sub framework,方便的开发人员在其构架上构建web层的应用系统。比如upload,collections ,logging等等。让我们来看看两个比较重要的组件:validationg framework和struts taglib。有关其他组件请参考Struts用户手册(http://jakarta.apache.org/struts/userGuide)。
在struts1.1中,新增了validation framework。增加了对form数据提交的验证。将原本需要在ActionFrom Bean的validate()进行的验证通过配置文件的描述进行验证。
有关其详细信息,请参考http://home.earthlink.net/~dwinterfeldt 。个人建议对于小型应用系统可以采用这种配置方式,但是对于应用系统中有大量web层表单应用的系统,并且业务需求变动比较大的,使用validation framework 可能会加重开发难度、系统维护难度。可以借鉴validation framework的Javascript Validator Tag。
struts提供了一组可扩展的自定义标签库(TagLib),可以简化创建用户界面的过程。目前包括:Bean Tags,HTML Tags,Logic Tags,Nested Tags,Template Tags 这几个Taglib。有关Struts Taglib的结构和使用,可以参考前面有关Cutomer Tag Lib的介绍,有关起详细资料,请参考
这个组件的全称是Bean Introspection Utilites。是属于Jakarta Commons项目组的。主要是帮助构建javabean的属性操作的(getter,setter),已经提供一种动态定义和访问bean的属性。有关详细信息,请参考。
http://jakarta.apache.org/commons/beanutils.html
如果各位对这方面有很兴趣,可以参考一些有关java反射(Reflectio)方面的资料。
这个组件主要是提供了一些集合或列表对象,在原有的java collections framework的基础上进行了扩展。详细资料请参考:
http://jakarta.apache.org/commons/collections.html 以及
http://cvs.apache.org/viewcvs/~checkout~/jakarta-commons/collections/STATUS.html?rev=1.13
这个组件翻译成中文的意思是“汇编”。其主要功能是根据xml配置文件,初始化系统的一些java类对象。Digester帮助你指定XML与java对象之间映射模型,而且允许客户话定制映射规则(rules)。详细资料请参考
http://jakarta.apache.org/commons/digester.html
Struts framework根据配置文件使得ServletAction,ActionMapping,Action , ActionForm这几个不同层次的组件相互交互,协调的工作。这些配置文件是在系统启动的时候,读入导内存中,供控制器使用的。
Struts framework主要包括三部分的配置描述,一个是指定有关Struts Controller及其相关的的配置描述(Initialization Parameters),一个对struts tag lib的描述,一个是struts组件(ActionMapping,Action,ActionForm)之间相互映射协调的关系
因为Struts Controller的主要类ActionServlet是继承自HttpServlet,所以必须像配置一个Servlet那样在部署描述符(Web.xml)中配置ActionServlet类及其访问映射。
当您第一次创建基于Struts的Web应用程序时,将为您创建一个部署描述符,这通常就足够了。该文件包括下列条目:
l
―
―
- “config”指示ActionServlet的行为由指定的配置文件来指导,该配置文件通常具有以下名称:
/WEB-INF/struts-config.xml
- “debug”具有整数值,它指示将有关处理的详细信息写至控制台的程度。
- ”detail”具有整数值,它指示将“映射”详细信息(如后面所述)写至控制台的程度。
―
l
― 访问了ActionServlet,原因是“操作”(
―
l
l
― 404 (找不到资源)
― 500 (Web服务器内部发生错误)
l 每个
如果你的web application打算使用Struts的taglib,那么你有必要在web.xml中对struts taglib进行配置描述。
作为先前描述的web.xml设置的结果,Web应用程序服务器将请求的一个子集按路径发送至ActionServlet,它通常调用一系列操作和JSP。ActionServlet的响应是基于配置文件struts-config.xml的内容的。有关其DTD文档的描述,请参考http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd
一般struts-config(version1.1)包含了如下几个部分:
(1)form-bean
(2)global-forwards
(3)action-mappings
(4)data-sources
我们知道,对于这样的一个请求(例如,表示为“/login.do”),执行下列步骤:
1、 寻找操作类对象(继承org. apache.struts.action.Action的类)
2、 ActionServlet调用操作类对象的执行方法
操作类中的执行方法的特征符为如下所示:
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
l 映射对象(ActionMapping),它包含指示如何响应方法的每个可能结果的规则(“映射”)
l Struts表单bean(ActionForm),它保存发送至HTML表单或接收自HTML表单的数据
l 请求和响应对象(HttpServletReques/ HttpServletResponse)
3、 从执行方法返回ActionForward对象,用于指导ActionServlet接着访问哪个操作类或JSP
返回的ActionForward对象中的信息取决于两个值:
l 方法的结果(如在“成功”或“故障”等字符串中所述)
l 映射对象,它包含从Struts配置文件中读取的信息
要弄明白某些运行时关系,要明白struts-config.xml该文件包括下面的一组条目:
l <form-beans>标记标识每个表单bean
l <action-mappings>标记包括用于指导应用程序流的信息,每个<action>子标记都使相对URL与操作类和潜在的后续操作相关。
Struts配置文件中的一个示例<form-bean>子元素为如下所示:
每个
name
表单bean的名称,稍后在配置文件中会用到。ActionServlet举例说明了该bean(如果需要的话)并在将对bean的引用存储在请求或会话对象中时将该名称用作键。
type
类的全限定名称,它继承org.apache.struts.action.ActionForm该类必须在类路径中。接受“Struts贸易样本”中的注册的表单bean包括HTML注册表单中每个字段的getter 和setter方法。该bean还包括验证方法,如下节“验证”中所述。
Struts配置文件中的一个示例<action>元素为如下所示:
type=“strutsEGL.RegisterAction”
name=“registerForm”
input=“/register.jsp”
scope=“request”
< forward name=“failure”path=“/register.jsp”/>
每个<action>元素都包括下列属性中的某些属性或所有属性:
path
将请求指定为非限定URL,不带文件扩展名(例如,“/register”)请求是根据<action>元素中的其它属性来处理的,并且是用户输入的结果或者是在different<action>元素中标识的转发的结果。
type
指定在发出请求时调用其执行方法的操作类的全限定名。该类必须在类路径中。
注:不指定要实例化的类,可以通过使用forward属性来转发请求,该属性在“Struts贸易样本”中未使用,并且与后面描述的<forward>子元素不相同。
name
用于保存发送至HTML表单或接收自HTML表单的数据表单bean的名称。
input
指定相对URL(例如,“/register.do”或“/index.jsp”)必须包括后缀,
如果表单bean的验证方法指示发生了输入错误,则会调用URL;有关详细信息,参见下节的“验证”。
scope
指定将对表单 bean的引用存储在哪个作用域中。其值为“会话”(缺省值)或“请求”。
Struts配置文件中的每个<action>元素还包括子元素<forward>,它指定从方法结果至后续调用的映射。每个<forward>子元素都包括下列属性
name
指定导致在运行时使用当前映射的字符串(例如,“success”),但是
只限于以下情况:在 type 中引用的操作类的执行方法使用完全相同
的字符串来配置返回至ActionServlet的 ActionForward对象。下面
的执行方法不是很重要,但是会导致使用“success”映射:
public ActionForward exectue(
ActionMapping mapping,
ActoinForm form,
HttpServletRequest request,
HttpServletResponse response)
Throws IOException,ServletException
{
ActionForward forward=new ActionForward();
Forward=mapping,findForward(“success”);
return(forward);
}
path
指定非限定URL(例如,“/home.do” 或“/index.jsp”)必须包括文件扩展名,仅当使用当前映射时才会调用该URL,转发操作类是根据different
有必要提一下的是,在struts1.1中,提出了对Multiple Application Support。在struts的早先版本中,只有一个struts配置文件,一般叫struts-config.xml。但是,对于越来越复杂的应用系统的发展,只有一个地方存放这个一个文件,对大型项目来说,使用和修改这个配置文件,使其成为了一个应用的瓶颈问题。在struts1.1中,你可以定义多了配置文件协同工作。
注:当用户或ActionServlet调用JSP时,请求是由Web应用程序服务器直接处理的不会受到ActionServlet的干预。
仅当在下列情况下才会在表单bean中对用户输入进行验证:
l 表单bean覆盖超类的验证方法
l Struts配置文件中的
Struts框架的各种部件使得可以进行验证
l Struts配置文件中的以下
type=“strutsEGL.RegisterAction”
name=“registerForm”
input=“/register.jsp”
scope=“request”
如果缺少验证属性,则意味着当ActionServlet接收到来自HTML表单对“/register”的请求时,ActionServlet将在接收用户数据的表单bean中调用验证方法。此验证在ActionServlet访问操作类之前进行。如果丢失了该方法,不会发生任何错误,在该情况下,验证总是会成功。
l 如果发生了错误,则表单bean的验证方法将举例说明错误类并将错误条目添加至该类。registerForm的验证方法的一个子集为如下所示:
ActionErrors errors=new ActionErrors();
If (username= =unll║username.equals(“”))
{
errors.add(“register”,
new ActionError(“error. Register.username”));
}
if (openingBalance<0.01)
{
errors.add(“register”,
new ActionError(“error. register. balance”));
}
return errors;
errors.add方法包括两个参数:
property
用来标识错误类别的Java字符串。
如果想要在特定的输入字段或输入字段的子集发生错误时
标识该错误,则指定属性值。例如,指定诸如“username”
之类的值的优点在于:报告了错误的JSP中,可以在屏幕上
靠近发生错误的字段的位置显示有关特定HTML字段的错
误消息,但是,要指示所有错误都属于同一类别,可以对属
性参数指定以下常量:
ActionErrors.GLOBAL_ERROR
error
包含从属性文件派生的“键-字符串”对的键的ActionError 对象。当配置ActionError对象时,最多可以包括要代入字符串中用来替代{0}、{1}等的四个值。
l 如果从验证方法返回了错误,则ActionServlet指导对在
l JSP register.jsp. 包括用于显示从验证方法派生的任何错误消息的以下
标记:
如果在未发生输入错误的情况下调用JSP,则该标记不显示任何内容,
而在JSP中将继续进行处理。但是,如果因发生验证故障而调用了JSP,
则为用户显示的内容将受到属性文件中是否包括下列键的影响:
― errors.header,它导致在所有错误消息前面显示一个字符串;或者
― errors.footer,它导致在所有错误消息后面显示一个字符串;或者
― errors.hiader,和errors.footer两者
例如,在ApplicationResources.properties 中,errors.header和 errors.footer的“键-字符串”对以及先前显示的这两个消息键为如下所示,它们各自都在单独的一行上(但是分成了多行显示以便于您复查):
errors.header=
The Action failed because of the following reason(s):
error.register.username=
error.register.balance=
Errors.footer=
如果在注册时用户对用户名输入了空白,对余额输入零,则用户将接收到一个包括两个错误的列表的屏幕:
The Action failed because of the following reason(s):
o You must enter a User ID.
o Your account must start with a positive balance.
可以为标记
要定义应用程序的逻辑流程,成熟的经验是推荐在代码之外,用配置的方法来实现,而不是写死在程序代码中的。在J2EE中,这样的例子比比皆是。从实现EJB的安全性和事务性行为到描述JMS消息和目的地之间的关系,很多运行时的处理流程都是可以在程序之外定义的。
Struts 创建者从一开始就采用这种方法,通过配置Struts的配置文件来定制应用系统运行时的各个方面。这一点在版本1.1的新特性上得到延续,包括新的异常处理功能。在Struts framework以前的版本中,开发人员不得不自己处理Struts应用中发生的错误情况。在最新的版本中,情况大大的改观了,Struts Framework提供了内置的一个称为 ExceptionHandler 的类,用于系统缺省处理action类运行中产生的错误。这也是在上一个技巧中我们提到的framework许多可扩展接口之一。
Struts缺省的 ExceptionHandler类会生成一个ActionError对象并保存在适当的范围(scope)对象中。这样就允许JSP页面使用错误类来提醒用户出现什么问题。如果你认为这不能满足你的需求,那么可以很方便的实现你自己的ExcepionHandler类。
具体定制异常处理的方法和机制
要定制自己的异常处理机制,第一步是继承org.apache.struts.action.ExceptionHandler类。这个类有2个方法可以覆盖,一个是excute()另外一个是storeException(). 在多数情况下,只需要覆盖其中的excute()方法。下面是ExceptionHandler类的excute()方法声明:
正如你看到的,该方法有好几个参数,其中包括原始的异常。方法返回一个ActionForward对象,用于异常处理结束后将controller类带到请求必须转发的地方去。
当然您可以实现任何处理,但一般而言,我们必须检查抛出的异常,并针对该类型的异常进行特定的处理。缺省的,系统的异常处理功能是创建一个出错信息,同时把请求转发到配置文件中指定的地方去。定制异常处理的一个常见的例子是处理嵌套异常。假设该异常包含有嵌套异常,这些嵌套异常又包含了其他异常,因此我们必须覆盖原来的execute()方法,对每个异常编写出错信息。
一旦你创建了自己的ExceptionHandler 类,就应该在Struts配置文件中的部分声明这个类,以便让Struts知道改用你自定义的异常处理取代缺省的异常处理.
可以配置你自己的ExceptionHandler 类是用于Action Mapping特定的部分还是所有的Action对象。如果是用于Action Mapping特定的部分就在元素中配置。如果想让这个类可用于所有的Action对象,可以在 元素中指定。例如,假设我们创建了异常处理类CustomizedExceptionHandler用于所有的Action类, 元素定义如下所示:
在元素中可以对很多属性进行设置。在本文中,最重要的属性莫过于handler属性, handler属性的值就是自定义的继承了ExceptionHandler类的子类的全名。假如该属性没有定义,Struts会采用自己的缺省值。当然,其他的属性也很重要,但如果想覆盖缺省的异常处理的话,handler无疑是最重要的属性。
最后必须指出的一点是,你可以有不同的异常处理类来处理不同的异常。在上面的例子中,CustomizedExceptionHandler用来处理任何java.lang.Exception的子类. 其实,你也可以定义多个异常处理类,每一个专门处理不同的异常树。下面的XML片断解释了如何配置以实现这一点。
在这里,一旦有异常抛出,struts framework将试图在配置文件中找到ExceptionHandler,如果没有找到,那么struts将沿着该异常的父类链一层层往上找直到发现匹配的为止。因此,我们可以定义一个层次型的异常处理关系结构,在配置文件中已经体现了这一点。
Struts 1.1的一个新特性是应用模块的概念。应用模块允许将单个Struts应用划分成几个模块,每个模块有自己的Struts配置文件,JSP页面,Action等等。这个新特性是为了解决大中型的开发队伍抱怨最多的一个问题,即为了更好的支持并行开发允许多个配置文件而不是单个配置文件。
显然,当很多开发人员一起参加一个项目时,单个的Struts配置文件很容易引起资源冲突。应用模块允许Struts按照功能要求进行划分,许多情况已经证明这样更贴近实际。例如,假设我们要开发一个典型的商店应用程序。可以将组成部分划分成模块比如catalog(商品目录), customer(顾客), customer service(顾客服务), order(订单)等。每个模块可以分布到不同的目录下,这样各部分的资源很容易定位,有助于开发和部署。图1 显示了该应用的目录结构。
一个典型的商店应用程序的目录结构
注:如果你无需将项目划分成多个模块,Struts框架支持一个缺省的应用模块。这就使得应用程序也可以在1.0版本下创建,具有可移植性,因为应用程序会自动作为缺省的应用模块。
为了使用多应用模块功能,必须执行以下几个准备步骤:
· 为每个应用模块创建独立的Struts配置文件。
· 配置Web 部署描述符 Web.xml文件。
· 使用org.apache.struts.actions.SwitchAction 来实现程序在模块之间的跳转.
创建独立的Struts配置文件
每个Struts应用模块必须拥有自己的配置文件。允许创建自己的独立于其他模块的Action,ActionForm,异常处理甚至更多。
继续以上面的商店应用程序为例,我们可以创建以下的配置文件:一个文件名为struts-config-catalog.xml,包含catalog(商品目录)、items(商品清单)、和其它与库存相关的功能的配置信息;另一个文件名为struts- config-order.xml, 包含对order(订单)和order tracking(订单跟踪)的设置。第三个配置文件是struts-config.xml,其中含有属于缺省的应用模块中的一般性的功能。
在为每个应用模块创建独立的配置文件之后,我们就有可能需要调用不同的模块中Action。为此必须使用Struts框架提供的SwitchAction类。Struts 会自动将应用模块的名字添加到URL,就如Struts 自动添加应用程序的名字加到URL一样。应用模块是对框架的一个新的扩充,有助于进行并行的团队开发。如果你的团队很小那就没必要用到这个特性,不必进行模块化。当然,就算是只有一个模块,系统还是一样的运作。
为了更好地保护你的JSP避免未经授权的访问和窥视, 一个好办法是将页面文件存放在Web应用的WEB-INF目录下。
通常JSP开发人员会把他们的页面文件存放在Web应用相应的子目录下。一个典型的商店应用程序的目录结构如图2所示。跟catalog (商品目录)相关的JSP被保存在catalog子目录下。跟customer相关的JSP,跟订单相关的JSP等都按照这种方法存放。
基于不同的功能 JSP 被放置在不同的目录下
这种方法的问题是这些页面文件容易被偷看到源代码,或被直接调用。某些场合下这可能不是个大问题,可是在特定情形中却可能构成安全隐患。用户可以绕过Struts的controller直接调用JSP同样也是个问题。
为了减少风险,可以把这些页面文件移到WEB-INF 目录下。基于Servlet的声明,WEB-INF不作为Web应用的公共文档树的一部分。因此,WEB-INF 目录下的资源不是为客户直接服务的。我们仍然可以使用WEB-INF目录下的JSP页面来提供视图给客户,客户却不能直接请求访问JSP。
采用前面的例子,图3显示将JSP页面移到WEB-INF 目录下后的目录结构
JSP存放在 WEB-INF 目录下更为安全
如果把这些JSP页面文件移到WEB-INF 目录下,在调用页面的时候就必须把"WEB-INF"添加到URL中。例如,在一个Struts配置文件中为一个logoff action写一个Action mapping。其中JSP的路径必须以"WEB-INF"开头。如下所示:请注意粗体部分.
这个方法在任何情况下都不失为Struts实践中的一个好方法。是唯一要注意的技巧是你必须把JSP和一个Struts action联系起来。即使该Action只是一个很基本的很简单JSP,也总是要调用一个Action,再由它调用JSP。
最后要说明的是,并不是所有的容器都能支持这个特性。WebLogic早期的版本不能解释Servlet声明,因此无法提供支持,据报道在新版本中已经改进了。总之使用之前先检查一下你的Servlet容器。
Struts framework带有好几个prebuilt Action类,使用它们可以大大节省开发时间。其中最有用的是org.apache.struts.actions.ForwardAction 和 org.apache.struts.actions.DispatchAction.
使用 ForwardAction
在应用程序中,可能会经常出现只要将Action对象转发到某个JSP的情况。在上一点中曾提到总是由Action调用JSP是个好习惯。如果我们不必在Action中执行任何业务逻辑,却又想遵循从Action访问页面的话,就可以使用ForwardAction,它可以使你免去创建许多空的Action类。运用ForwardAction的好处是不必创建自己的Action类,你需要做的仅仅是在Struts配置文件中配置一个Action mapping。
举个例子,假定你有一个JSP文件index.jsp ,而且不能直接调用该页面,必须让程序通过一个Action类调用,那么,你可以建立以下的Action mapping来实现这一点:
正如你看到的,当 /home 被调用时, 就会调用ForwardAction 并把请求转发到 index.jsp 页面.
再讨论一下不通过一个Action类直接转发到某个页面的情况,必须注意我们仍然使用元素中的forward属性来实现转发的目标。这时元素定义如下:
以上两种方法都可以节省你的时间,并有助于减少一个应用所需的文件数。
使用 DispatchAction
DispatchAction是Struts包含的另一个能大量节省开发时间的Action类。与其它Action类仅提供单个execute()方法实现单个业务不同,DispatchAction允许你在单个Action类中编写多个与业务相关的方法。这样可以减少Action类的数量,并且把相关的业务方法集合在一起使得维护起来更容易。
要使用DispatchAction的功能,需要自己创建一个类,通过继承抽象的DispatchAction得到。对每个要提供的业务方法必须有特定的方法signature。例如,我们想要提供一个方法来实现对购物车添加商品清单,创建了一个类ShoppingCartDispatchAction提供以下的方法:
那么,这个类很可能还需要一个deleteItem()方法从客户的购物车中删除商品清单,还有clearCart()方法清除购物车等等。这时我们就可以把这些方法集合在单个Action类,不用为每个方法都提供一个Action类。
在调用ShoppingCartDispatchAction里的某个方法时,只需在URL中提供方法名作为参数值。就是说,调用addItem()方法的 URL看起来可能类似于:
http://myhost/storefront/action/cart?method=addItem
其中method参数指定ShoppingCartDispatchAction中要调用的方法。参数的名称可以任意配置,这里使用的"method"只是一个例子。参数的名称可以在Struts配置文件中自行设定。
Struts提供了用来封装逻辑的各种定制JSP标记,因此页面设计者可以将主要精力花在页面的可视特征上,而不必主要考虑Java语法或其它JSP语法,在下列标识库描述符中引用了Struts标记:
Struts-bean.tld
使访问bean以及新bean的定义更容易,,为了实现国际化,应使用不同的属性文件
struts-html.tld
提供显示HTML对象(例如,表单、按钮和复选框)的简便方法
struts-logic.tld
支持逻辑构造,以便可以有条件地显示文本或者作为处理循环的结果来显示文本
struts-template.tld
支持使用在运行时可以修改的JSP模板
要在JSP文件顶部的<taglib>伪指令如下所示:
<%@ taglib uri=“struts-html. tld” prefix=“html”%>
<%@ taglib uri=“struts-bean.tld”prefix=“bean”%>
<%@ taglib uri=“struts-logic.tld”prefix=“logic”%>
每个<taglib>伪指令都具有与基于 web.xml的< taglib>标记中的URL相匹配的URL。另外JSP中的每个 struts标记都使用一个使标记与特定标记库描述符相关的前缀:
― 没有嵌套内容的标记可以采用以下格式:
― 嵌套内容是在一对标记之间嵌套的:
prefix
在JSP taglib伪指令中指定的前缀
tagName
标记的名称,如标记库描述符中所述;描述符条目指定提供标记逻辑的Jave类
attributesAndValues
― 系列属性与值的配对(是必需的或者是可选的),每个配对都包括一种属性、一个等号(没有前导或结尾空白)和一个引起来的字符串
文件resource.jsp包含bean:message标记的几个实例。以下是标记的示例用法:
在最简单的情况下,bean:message标记解析为存储在根据属性文件创建的资源束中的字符串:
― 属性文件的名称是用来调用ActoinServlet的web.xml “application”参数的值。如:
/WEB-INF/classes/ApplicationResources.properties
― 消息标记中的key属性指向属性文件中的“键-字符串”对;在本例中,指向下面的“键-字符串”对:
market. text.title=Current Market Conditions
可以采用各种方法来定制bean:message标记,以便(例如)JSP在运行时引用不同的属性文件。标记提供了一种方法来支持多种语言以及最多将四个替代值插入到字符串中来代替{0}、{1}等等。
l 仅当指定的对象或值存在时,logic:present 标记才会导致显示嵌套的文本。在register.jsp中,仅当操作类创建了作为 tickerBean引用(在任何作用域中)的 Java bean 时才为用户提供HTML表行。 Struts标记为如下所示:
-->nested content for presentation<--
l Struts标记允许很方便地访问Java bean内容。例如,以下标记将解析为存储在 tickerBean中的值:
tickerBean的源代码在以下目录中:
Trade/Java Source/tradeCommon/tickerBean.java
l HTML表单与表单bean之间的数据传送是通过使用html:form 和html:text标记来完成的。 register.jsp中的输入表单是按如下所示构建的:
-->nested form content with html: text tags<--
html:form 标记解析为HTML FORM 标记并导致html: text 标记引用适当的表单bean;特别是在 path=“/register”的 Struts配置文件的
html:text标记建立HTML输入字段。例如,以下标记确保在HTML输入字段与表单bean的用户名字段之间传送信息:
JSP视窗组件所使用的 struts标记库由四类标记组成:
l Bean标记:用来在JSP页中管理bean
l 逻辑标记:用来在JSP页中控制流程
l HTML标记:用来生成HTML标记,在表单中显示数据,使用会话ID对URL进行编程
l 模板标记:使用动态模板构造普通格式的页
这个标记库中包含用于定义新bean、访问bean及其属性的标记。Struts框架提供了多种自定义标记用来在JSP页中处理JavaBean。这些标记被封装在一个普通的标记库中,在文件struts-bean.tld中定义了它的标记库描述器。Bean标记库将标记定义在四个子类别中:
l 创建和复制bean的标记
l 脚本变量定义标记
l bean翻译标记
l 消息国际化标记
可定义新bean,可复制现有bean,还可从现有bean复制属性。
l 定义新字符串常数
l 将现有的bean复制到新定义的bean对象
l 复制现有bean的属性来创建新的bean
属性 |
描述 |
Id |
新定义的bean脚本变量名称,必须设置 |
Type |
定义引入脚本变量的类 |
Value |
为id属性定义的脚本变量分配一个新的对象 |
Name |
目标bean的名称。若value属性没有设置,这个属性就必须设置 |
property |
Name属性定义的bean的属性名称,用来定义新的bean |
Scope |
源bean的作用域。若没有设置,搜索范围是从页作用域到应用程序作用域 |
toScope |
目标bean的作用域。若没有设置,默认值是页作用域 |
例如:定义一个bean:
源bean在页作用域中被拷贝大哦请求作用域中的另一个bean:
scope=”page” toScope=”request”/>
从多种资源中定义和生成脚本变量,这些资源包括cookie,请求参数,HTTP标头等等。属性如下:
属性 |
描述 |
Id |
脚本变量和要定义的页作用域属性的名称 |
Name |
cookie/标头/参数的名称 |
multiple |
如果这个属性设置了任意一个数值,所有匹配的cookie都会被积累并存储到一个Cookie[](一个数组)类型的bean里。若无设置,指定cookie的第一个值将作为Cookie类型的值 |
Value |
如果没有匹配的cookie或数值,就返回这个属性指定的默认值 |
例如:
脚本变量名称是myCookie,用来创建这个属性的cookie的名称是userName。
脚本变量名称是myHeader,请求标头的名称是Accept-Language.
脚本变量名称是myPatameter,它保存的请求参数的名称也是myParameter.
属性 |
描述 |
Id |
脚本变量和要定义的页作用域属性的名称 |
Page |
一个内部资源 |
forward |
一个ActionForward |
Href |
要包含的资源的完整URL |
例如:
脚本变量的名称是myInclude,要检索的响应来自资源MyJsp?x=1。
属性 |
描述 |
Id |
脚本变量和要定义的页作用域属性的名称 |
Name |
资源的相对路径 |
Input |
如果这个属性不存在,资源的类型就是字符串 |
例如:
脚本变量的名称是myResource,要检索的资源的名称是myResource.xml。
标记库中定义了
属性 |
描述 |
Name |
要进行属性显示的bean的名称 |
property |
要显示的属性的名称。如果这个属性类有java.beans.PropertyEditor,getAsText()或toString 方法会被调用 |
Scope |
Bean的作用域,若没有设置,搜索范围是从页到应用程序作用域 |
Filter |
如果设置true,属性中的所有特殊HTML字符都将被转化为相应的实体引用 |
Ignore |
如果设置false,当发现属性时会产生一个请求时间异常,否则返回null |
例如:
filter=”true”/>
myBean的属性myProperty将会被显示,作用域为请求,如果发现任何HTML特殊字符都将被转化为相应的实体引用。
strtus框架支持国际化和本地化。用户在他们的计算机中定义自己所在的区域,当web应用程序需要输出一条消息时,它将引用一个资源文件,在这个文件中所有的消息都使用了适当的语言。一个应用程序可能提供了很多资源文件,每个文件提供了用不同语言编写的消息。如果没有找到所选语言的资源文件,就将使用默认的资源文件。
struts框架对国际化的支持是使用
用strtus实现国际化和本地化:
第一步要定义资源文件的名称,这个文件会包含用默认语言编写的在程序中会出现的所有消息。这些消息以“关键字-值”的形式存储,如下:
error.validation.location = The entered location is invalid
这个文件需要存储在类的路径下,而且它的路径要作为初始化参数传送给ActionServlet作为参数进行传递时,路径的格式要符合完整Java类的标准命名规范。比如,如果资源文件存储在WEB-INF/classes目录中,文件名是ApplicationResources.properties,那么需要传递的参数值是ApplicationResources。如果文件在WEB-INF/classes/com/test中,那么参数值就应该是com.test. ApplicationResources.
为了实现国际化,所有的资源文件必须都存储在基本资源文件所在的目录中。基本资源文件包含的是用默认地区语言-本地语言编写的消息。如果基本资源文件的名称是ApplicationResources.properties,那么用其他特定语言编写的资源文件的名称就应该是ApplicationResources_xx.properties(xx为ISO编码,如英语是en)。因此这些文件应包含相同的关键字,但关键字的值是用特定语言编写的。
ActionServlet的区域初始化参数必须与一个true值一起传送,这样ActionServlet就会在用户会话中的Action.LOCALE_KEY关键字下存储一个特定用户计算机的区域对象。现在可以运行一个国际化的web站点,它可以根据用户计算机上的设置的区域自动以相应的语言显示。
我们还可以使用特定的字符串来替换部分消息,就象用java.text.MessageFormat的方法一样:
error.invalid.number = The number {0} is valid
我们可以把字符串{0}替换成任何我们需要的数字。
属性 |
描述 |
Key |
资源文件中定义消息关键字 |
Locale |
用户会话中存储的区域对象的属性名称。若没有设置,默认值是Action.LOCALE_KEY |
Bundle |
在应用程序上下文中,存储资源对象的属性的名称。如果没有设置这个属性,默认值是Action.MESSAGE_KEY |
arg0 |
第一个替换参数值 |
arg1 |
第二个替换参数值 |
arg2 |
第三个替换参数值 |
arg3 |
第四个替换参数值 |
例如:资源文件中定义了一个消息:
info.myKey = The numbers entered are {0},{1},{2},{3}
我们可使用下面的消息标记:
这个信息标记输出到JSP页会显示为:The numbers entered are 5,6,7,8
逻辑库的标记能够用来处理外观逻辑而不需要使用scriptlet。Struts逻辑标签库包含的标记能够有条件地产生输出文本,在对象集合中循环从而重复地产生输出文本,以及应用程序流程控制。它也提供了一组在JSP页中处理流程控制的标记。这些标记封装在文件名为struts-logic.tld的标记包中。逻辑标记库定义的标记能够执行下列三个功能:
l 条件逻辑
l 重复
l 转发/重定向响应
struts有三类条件逻辑。第一类可以比较下列实体与一个常数的大小:
l cookie
l 请求参数
l bean或bean的参数
l 请求标头
以下列出了这一类标记:
标记 |
功能 |
如果常数与被定义的实体相等,返回true |
|
如果常数与被定义的实体不相等,返回true |
|
如果常数大于等于被定义的实体,返回true |
|
如果常数小于等于被定义的实体,返回true |
|
如果常数小于被定义的实体,返回true |
|
如果常数大于被定义的实体,返回true |
这一类的所有标记有相同的属性
属性 |
描述 |
Value |
要进行比较的常数值 |
Cookie |
要进行比较的HTTP cookie的名称 |
Header |
要进行比较的HTTP请求标头的名称 |
parameter |
要进行比较的HTTP请求参数的名称 |
Name |
如果要进行比较的是bean或bean的属性,则这个属性代表bean的名称 |
property |
要进行比较的bean属性的名称 |
Scope |
Bean的作用域,如果没有指定作用域,则它的搜索范围是从页到应用程序 |
例如:
The entered name is SomeName
判断名为”name”的请求参数的值是否是”SomeName”。
The value of bean.Prop is greater than 7
判断在页的作用域中是否有一个名为”bean”的bean,它有一个prop属性,这个属性的值是否大于7。如果这个属性能够转化为数值,就进行数值比较,否则就进行字符串比较。
第二类条件标记定义了两个标记:
l
l
它们的功能是在计算标记体之前判断特定的项目是否存在。标记的属性和属性值决定了要进行检查的项目。
属性 |
描述 |
Cookie |
由这个属性指定的cookie将被检查是否存在 |
Header |
由这个属性指定的请求标头将被检查是否存在 |
parameter |
由这个属性指定的请求参数将被检查是否存在 |
Name |
如果没有设置property属性,那么有这个属性指定的bean将被检查是否存在。如果设置了,那么bean和bean属性都将被检查是否存在。 |
property |
检查有name属性指定的bean中是否存在指定的属性 |
Scope |
如果指定了bean的名称,这就是bean的作用域。如果没有指定作用域,搜索的范围从页到应用程序作用域。 |
Role |
检查当前已经确认的用户是否属于特殊的角色 |
User |
检查当前已经确认的用户是否有特定的名称 |
例如:
The bean property bean.prop is present
标记判断在页作用域中是否存在一个名为”bean”的bean,这个bean有一个prop属性。
第三类条件标记比较复杂,这些标记根据模板匹配的结果检查标记体的内容。换句话说,这些标记判断一个指定项目的值是否是一个特定常数的子字符串:
l
l
这些标记允许JSP引擎在发现了匹配或是没有发现时计算标记主体。属性如下:
属性 |
描述 |
Cookie |
要进行比较的HTTP cookie的名称 |
Header |
要进行比较的的HTTP标头 的名称 |
parameter |
要进行比较的的HTTP请求参数的名称 |
Name |
若要对bean或bean的属性进行比较,这个属性是用户指定bean的名称 |
location |
如果设置了这个属性的值,将会在这个指定的位置(索引值)进行匹配 |
scope |
如果对bean进行比较,这个属性指定了bean的作用域。如果没有设置这个参数,搜索范围是从页到应用程序作用域 |
property |
要进行比较的bean的属性名称 |
value |
要进行比较的常数值 |
例如:
The parameter name is a sub-string of the string xyz from index 1
标记检查名为”name”的请求参数是否是”xyz”的子字符串,但是子字符串必须从”xyz”的索引位置1开始(也就是说子字符串必须是”y”或”yz”)。
在逻辑标记库中定义了
,java.util.Map或是一个数组。有三种方法可以定义这个集合:
l 使用运行时间表达式来返回一个属性集合的集合
l 将集合定义为bean,并且使用name属性指定存储属性的名称。
l 使用name属性定义一个bean,并且使用property属性定义一个返回集合的bean属性。
当前元素的集合会被定义为一个页作用域的bean。属性如下,所有这些属性都能使用运行时表达式。
属性 |
描述 |
collection |
如果没有设置name属性,它就指定了要进行重复的集合 |
Id |
页作用域bean和脚本变量的名称,它保存着集合中当前元素的句柄 |
indexed |
页作用域JSP bean的名称,它包含着每次重复完成后集合的当前索引 |
Length |
重复的最大次数 |
Name |
作为集合的bean的名称,或是一个bean名称,它由property属性定义的属性,是个集合 |
Offset |
重复开始位置的索引 |
property |
作为集合的Bean属性的名称 |
Scope |
如果指定了bean名称,这个属性设置bean的作用域。若没有设置,搜索范围从页到应用程序作用域 |
Type |
为当前定义的页作用域bean的类型 |
例如:
collection=”<% =myList %>”
type=”java.lang.Integer”
offset=”1”
length=”2”>
<% =currentint %>
代码将从列表中的第一个元素开始重复两个元素并且能够让当前元素作为页作用域和java.lang.Integer类型的脚本变量来使用。也就是说,如果myList包含元素1,2,3,4等,代码将会打印1和2。
转发标记
重定向标记
属性 |
描述 |
Forward |
映射了资源相对路径的ActionForward |
Href |
资源的完整URL |
Page |
资源的相对路径 |
Name |
Map类型的页名称,请求,会话或程序属性的名称,其中包含要附加大哦重定向URL(如果没有设置 property属性)上的“名称-值”参数。或是具有Map类型属性的bean名称,其中包含相同的信息(没有设置property属性) |
Property |
Map类型的bean属性的名称。Bean的名称由name属性指定。 |
Scope |
如果指定了bean的名称,这个属性指定搜索bean的范围。如果没有设置,搜索范围从页到应用程序作用域 |
ParamID |
定义特定查询参数的名称 |
ParamName |
字符串类型的bean的名称,其中包含查询参数的值(如果没有设置paramProperty属性);或是一个bean的名称,它的属性(在paramProperty属性中指定)包含了查询参数值 |
paramProperty |
字符串bean属性的名称,其中包含着查询参数的值 |
ParamScope |
ParamName定义的bean的搜索范围 |
使用这个标记时至少要指定forward,href或page中的一个属性,以便标明将响应重定向到哪个资源。
Struts HTML标记可以大致地分为以下几个功能:
l 显示表单元素和输入控件
l 显示错误信息
l 显示其他HTML元素
struts将HTML表单与为表单操作而定义的ActionForm bean紧密联系在一起。表单输入字段的名称与ActionForm bean里定义的属性名称是对应的。当第一次显示表单时,表单的输入字段是从ActionForm bean中移植过来的,当表单被提交时,请求参数将移植到ActionForm bean实例。
所有可以在标记中使用的用来显示HTML输入控件的内嵌标记都使用下列属性来定义JavaScript事件处理器。
属性 |
描述 |
Onblur |
字段失去了焦点 |
Onchange |
字段失去了焦点并且数值被更改了 |
Onclick |
字段被鼠标点击 |
Ondblclick |
字段被鼠标双击 |
Onfocus |
字段接收到输入焦点 |
Onkeydown |
字段拥有焦点并且有键按下 |
onkeypress |
字段拥有焦点并且有键按下并释放 |
Onkeyup |
字段拥有焦点并且有键被释放 |
onmousedown |
鼠标指针指向字段并且点击 |
onmousemove |
鼠标指针指向字段并且在字段内移动 |
onmouseout |
鼠标指针指向控件,但是指针在元素外围移动 |
onmouseover |
鼠标指针没有指向字段,但是指针在元素内部移动 |
Onmouseup |
鼠标指针指向字段,并且释放了鼠标按键 |
元素中能够被定义的其他一般属性有:
属性 |
描述 |
Accesskey |
定义访问输入字段的快捷键 |
Style |
定义输入字段的样式 |
styleClass |
定义输入字段的样式表类 |
Tabindex |
输入字段的tab顺序 |
表单标记
标记能够包含与各种HTML输入字段相对应的子标记。
属性 |
描述 |
Action |
与表单相关的操作。在配置中,这个操作也用来标识与表单相关的ActionForm bean |
Enctype |
表单HTTP方法的编码类型 |
Focus |
表单中需要初始化焦点的字段 |
Method |
表单使用的HTTP方法 |
Name |
与表单相关的ActionForm bean的名称。如果没有设置这个属性,bean的名称将会从配置信息中获得 |
Onreset |
表单复位时的JavaScript事件句柄 |
Onsubmit |
表单提交时的JavaScript事件句柄 |
Scope |
搜索ActionForm bean的范围。如果没有设置,将从配置文件中获取 |
Style |
使用的格式 |
styleClass |
这个元素的格式表类 |
Type |
ActionForm bean的完整名称。如果没有设置,将从配置文件获得 |
例如:
与表单相关的操作路径是validateEmployee,而表单数据是通过POST传递的。对于这个表单来说,ActionForm bean的其他信息,如bean名称类型,作用域,都是从表单指定操作的ActionMapping中检索得到的:
type=”com.example.ValidateExampleAction”
name=”empForm”
scope=”request”
input=”/employeeInput.jsp”>
如果配置文件中包含上述信息,并且请求URI的*.do被映射到ActionServlet,与表单相关的ActionForm bean的名称,类型和作用域分别是empForm,com.example.EmployeeForm和request.这些属性也可以使用
以下标记必须嵌套在
按钮和取消标记
属性 |
描述 |
Property |
定义在表单被提交时返回到服务器的请求参数的名称 |
Value |
按钮上的标记 |
复位和提交标记
文本和文本区标记
属性 |
描述 |
Property |
定义当表单被提交时送回到服务器的请求参数的名称,或用来确定文本元素当前值的bean的属性名称 |
Name |
属性被查询的bean的名称,它决定了文本框和文本区的值。如果没有设置,将使用与这个内嵌表单相关的ActionForm的名称 |
属性 |
描述 |
Maxlength |
能够输入的最大字符数 |
Size |
文本框的大小(字符数) |
属性 |
描述 |
Rows |
文本区的行数 |
Cols |
文本区的列数 |
检查框和复选框标记
属性 |
描述 |
Name |
Bean的名称,其属性会被用来确定检查是否以选中的状态显示。如果没有设置,将使用与这个内嵌表单相关的ActionFrom bean的名称。 |
Property |
检查框的名称,也是决定检查框是否以选中的状态显示的bean属性名称。在复选框的情况下,这个属性必须是一个数组。 |
Value |
当检查框被选中时返回到服务器的请求参数的值 |
例如:
一个名为married的检查框,在表单提交时会返回一个”Y”.
文件标记
属性 |
描述 |
Name |
Bean的名称,它的属性将确定文件控件中显示的内容。如果没设置,将使用与内嵌表单相关的ActionForm bean的名称 |
property |
这个属性定义了当表单被提交时送回到服务器的请求参数的名称,以及用来确定文件控件中显示内容的bean属性名称 |
Accept |
服务器能够处理的内容类型集。它也将对客户浏览器对话框中的可选文件类型进行过滤 |
Value |
按钮上的标记,这个按钮能够在本地文件系统中浏览文件 |
单选钮标记
属性 |
描述 |
Name |
Bean的名称,其属性会被用来确定单选钮是否以选中的状态显示。如果没有设置,将使用与这个内嵌表单相关的ActionFrom bean的名称。 |
property |
当表单被提交时送回到服务器的请求参数的名称,以及用来确定单选钮是否以被选中状态进行显示的bean属性的名称 |
Value |
当单选钮被选中时返回到服务器的值 |
隐藏标记
属性 |
描述 |
Name |
Bean的名称,其属性会被用来确定隐藏元素的当前值。如果没有设置,将使用与这个内嵌表单相关的ActionFrom bean的名称。 |
property |
定义了当表单被提交时送回到服务器的请求参数的名称,以及用来确定隐藏元素当前值的bean属性的名称 |
Value |
用来初始化隐藏输入元素的值 |
密码标记
属性 |
描述 |
maxlength |
能够输入的最大字符数 |
Name |
Bean的名称,它的属性将用来确定密码元素的当前值。如果没有设置,将使用与这个内嵌表单相关的ActionFrom bean的名称。 |
property |
定义了当表单被提交时送回到服务器的请求参数的名称,以及用来确定密码元素当前值的bean属性的名称 |
redisplay |
在显示这个字段时,如果相应的bean属性已经被设置了数据,这个属性决定了是否显示密码的内容 |
Size |
字段的大小 |
选择标记
属性 |
描述 |
multiple |
表明这个选择控件是否允许进行多选 |
Name |
Bean的名称,它的属性确定了哪个。如果没有设置,将使用与这个内嵌表单相关的ActionFrom bean的名称。 |
property |
定义了当表单被提交时送回到服务器的请求参数的名称,以及用来确定哪个选项需要被选中的bean属性的名称 |
Size |
能够同时显示的选项数目 |
Value |
用来表明需要被选中的选项 |
选项标记(这个元素需要嵌套在
属性 |
描述 |
collection |
Bean集合的名称,这个集合存储在某个作用域的属性中。选项的数目与集合中元素的数目相同。Property属性能够定义选项值所使用的bean属性,而labelProperty属性定义选项标记所使用的bean的属性 |
labelName |
用来指定存储于某个作用域的bean,这个bean是一个字符串的集合,能够定义 |
labelProperty |
与collection属性共同使用时,用来定义了存储于某个作用域的bean,这个bean将返回一个字符串集合,能够用来写入 |
Name |
如果这是唯一被指定的属性,它就定义了存储于某个作用域的bean,这个bean将返回一个字符串集合,能够用来写入 |
property |
这个属性在与collection属性共同使用时,定义了每个要显示选项值的独立bean的name属性。如果不是与collection属性共同使用,这个属性定义了由name属性指定的bean的属性名称(如果有name属性),或是定义了一个ActionForm bean,这个bean将返回一个集合来写入选项的值 |
我们看一下这个标记的一些例子:
labelProperty=”optionLabel”/>
标记假设在某个作用域中有一个名为optionCollection的集合,它包含了一些具有optionValue属性的独立的bean,每个属性将作为一个选项的值。每个选项的标志由bean的optionLabel属性属性进行定义。
标记中optionValues代表一个存储在某个作用域中的bean,它是一个字符串集合,能够用来写入选项的值,而optionLabels代表一个存储在某个作用域中的bean,它也是一个字符串集合,能够用来写入选项的标志。
通过定义property属性能够过滤要显示的消息,这个属性的值应该与ActionErrors对象中存储ActionError对象的关键字对应。属性如下:
属性 |
描述 |
Bundle |
表示应用程序作用域属性的名称,它包含着消息资源,其默认值Acion.MESSAGE_KEY |
Locale |
表示会话作用域属性的名称,它存储着用户当前登录的区域信息。其默认值是Action.ERROR_KEY |
Name |
表示请求属性的名称,它存储着ActionErrors对象。其默认值是Action.ERROR_KEY |
property |
这个属性指定了ActionErrors对象中存储每个独立ActionError对象的关键字,它可以过滤消息 |
例子:
显示集合中所有的错误。
显示存储在missing.name关键字的错误。
struts HTML标记还定义了下列标记来显示其他HTML元素:
l
l
l
l
这些标记的详细内容请参照struts文档。
动态模板是模块化WEB页布局设计的强大手段。Struts模板标记库定义了自定义标记来实现动态模板。
插入标记
放置标记
属性 |
描述 |
content |
定义要插入的内容,比如一个JSP文件或一个HTML文件 |
direct |
如果这个设置为true,由content属性指定的内容将直接显示在JSP上而不是作为包含文件 |
Name |
要插入的内容的名称 |
Role |
如果设置了这个属性,只有在当前合法用户具有特定角色时才能进行内容的插入。 |
获得标记
在模板JSP页中使用
属性 |
描述 |
Name |
由 |
Role |
如果设置了这个属性,只有在当前合法用户具有特定角色时才能进行内容的检索 |
使用模板标记
首先编写一个模板JSP页,它将被所有的web页使用:
<%@ taglib uri=”/template” prefix=”template” %>
我们将这个文件命名为template.jsp。这个文件使用
<%@ taglib uri=”/template” prefix=”/template” %>
这个应用程序JSP页使用
在这个指导中我们将step by step开发一个小的应用程序。你应该有一些JSP和XML的经验,并且有一个可以运行的应用服务器。
请先将Struts.jar和所有相关common拷贝到你应用程序的lib目录中,不用删除你的struts目录中的其他文件。结果如图1所示。
现在我们要做一个简单的JSP页,用来确认至此我们的操作是正确的。
在strutsShop里建一个BookView.jsp的文件。内容如下,按图2所示在浏览器中运行:
我们将使用户能用本国的语言浏览预定义的文本,进而接触一些struts的功能。首先你要按照图3拷贝一些文件到WEB-INF目录下。在struts-html.tld文件里有我们要用的标签。这些我们在上接已经介绍了,你可以简短的回顾一下。
struts-config.xml的配置清单如下,这是一个标准的清单,你以后的程序都可以以此为基础进行扩展:
web.xml的配置清单如下,这是一个标准的清单,你以后的程序都可以以此为基础进行扩展:
在classes目录下创建一个ApplicationResources.properties的文件(此文件名在web.xml中定义),打开它,输入一行:index.title=Struts Tutorial。然后在创建一个ApplicationResources_de.properties文件,也输入一行:index.title=Struts Einführung。其实这两个文件就是当加载时会根据当前的浏览器而选择英文或德文,这里我们只能了解国际化过程来测试前者了。我们还需编写BookView.jsp文件,如下:
可在浏览器中浏览,你可能需要重启你的tomcat:
其实我们可以将国际化写在一个文件中,就是将参数写在一个属性文件中。(这是作者的意图,但我没有各种版本的IE进行实验)
在这一章我们将创建一个简单的Bean(Book.java)和两个JSP页面,一个是创建新书的,另一个是显示它的,我们也会第一次使用struts-config.xml文件。
我们先在你的classes目录下创建如下Book.java文件。
我们还需要创建新书的JSP页。我们将使用title,auther和number of pages三个字段,在此之前我们先要做一些工作,对于一个初学者这将有些难度。在你的BookView中加上以下内容:
再次运行,你将得到图5所示内容,如果没有错,那你需要重启tomcat;如果错误不一样,没关系,因为他都是没有在配置文件中找到mapping路径。
接下来我们需要第二个JSP页面CreateBook.jsp,代码如下:
在classes目录下创建一个BookAction文件:
它没按照struts要求编写仅仅创建一本书并给它标题。然后编写你的struts-config.xml:
我们希望在struts中在bookForm和Book间建立连接。而且我们还用bookCreated定义了一个到BookView.jsp的转发。最后我们用action=createBook.do属性定义了我们的form做什么。关于do:与接受CreateBook.jsp输入信息的bookForm相关的bean,由createBook命令创建。
按图6编译你的类。由于我是初手,在这里遇见很多问题,所以耽误了很久,不过它让你学到很多东东。比如:javax.servlet要用到servlet.jar包;javac后跟a.java, 而java后跟a;还有我遇见了很原文提到的问题,很多都是由于自己编写(没有copy)而造成的马虎。希望大家也能引起注意,到此除了ActionForm我们都已用到了。
编译成功后,在你的classes目录下会增加两个文件:Book.class和BookAction.class.
重启你的tomcat(每次改动config文件你都需要重启,改动注册表需要重启机器)。现在在你的浏览器里登陆CreateBook.jsp,如图7:
当你填写后提交,另你失望的是什么也没有得到。这是由于我们并没有ActionForm bean.
这节我们继续做ActionForm bean来完成我们的例子。我们将用ActionForm得到合法的book的信息,并进行一些检验,例如没有输入标题等,向用户提供错误或成功的信息。我们还将得到book的属性并且能够更改它。
为此我们需要一个ActionForm:它仅是一个简单的容器,没有应用程序逻辑,只有两个方法:reset(),validate().在struts1.1里,validate()方法被单独作为一个validate.xml文件。详见参考资料二。
现在我们做另一个类:BookForm.java。它将包含book的实例,并且有一些getXXX和setXXX的方法来访问它。关于内部的方法可看struts架构介绍。
我们还要做一些额外的工作。去看struts-config.xml文件,我们需要用这个新类与Form关联,从而替代Book.java。因此我们必须改变form-beans:
另外我们还有定义一下当错误发生时的信息,在你的配置文件中输入:error.book.title=Error
现在编译你的类,重新启动tomcat,重新登陆CreateBook.jsp.,输入onebook,你将在tomcat的dos窗口看见如下图所示:
你也可以在重输入其他的title,看看tomcat的dos窗口有什么变化。
上一节我们使用一个新类BookForm.java去访问Book.java,而不用struts直接连接到Book.java。接下来,我们要解决在这两个类中重复输入getXXX和setXXX。Struts允许我们直接访问实例的方法。这很容易,但需要理解。我们给出改变的CreateBook.jsp的代码:
正如你看到的,除了将title改为book.title,我们什么也没做。现在你可以去掉BookForm.java中的getXXX和setXXX方法了,如下图:(别忘记了也更改BookView.jsp,否则在你的tomcat窗口下After creation of book : null)
你还应该改动BookAction.java,把String title = req.getParameter("title");中的title改为book.title,然后重新编译,这样你的tomcat下的After creation of book :none中的none就会出现你输入的名了。