Struts中文用户指南(1分)

  • 主题发起人 主题发起人 曹晓钢
  • 开始时间 开始时间

曹晓钢

Unregistered / Unconfirmed
GUEST, unregistred user!

--------------------------------------------------------------------------------

快速连接
Home
目录
简介
Model 部件
View 部件
Controller 部件
资源
我们是谁
0. Contents
Table of Contents
1. 简介
1.0 先决条件
1.1 前言: 回到从前!
1.2 Model-View-Controller ('MVC') 设计模式
1.3 Struts 框架纵览
1.4 Model(模型): 系统状态和商业逻辑JavaBeans
1.5 View(视图): JSP页面和表示部件
1.6 Controller(控制器): ActionServlet 和 ActionMapping
2. 构造Model(模型)部件
2.1 纵览
2.2 JavaBeans 和 scope(范围)
2.3 ActionForm Beans
2.4 系统状态Beans
2.5 商业逻辑 Beans
2.6 访问关系数据库
3. 构造 View (视图)部件
3.1 纵览
3.2 Internationalization
3.3 Forms 和 FormBean 的交互
3.3.1 B在Struts里编写Form
3.3.2 支持的Input 字段的类型
3.3.3 其它有用的表示层标签
3.3.4 自动form检查
3.4 其他表示层技术
3.4.1 针对程序的自定义标签
3.4.2 用Include(引用)来组合页面
3.4.3 图像生成部件
4. 构造 Controller (控制器)部件
4.1 纵览
4.2 Action 类
4.3 ActionMapping 的实现
4.4 Action Mappings 配置文件
4.5 Web 程序发布描述
4.5.1 配置Action servlet实例
4.5.2 配置Action Servlet 映射
4.5.3 配置 Struts 标签库
4.6 把Struts部件增加到你的程序中
5. 资源
6. 我们是谁


--------------------------------------------------------------------------------

Copyright (c) 2000-2002, Apache Software Foundation
 

--------------------------------------------------------------------------------

快速连接
Home
目录
简介
Model 部件
View 部件
Controller 部件
资源
我们是谁
1. 简介
1.0 先决条件
这份用户指南是为那些活跃的Web开发者编写的,假设读者已经了解java Web application是如何工作的。在开始之前,您应该已经掌握下列基本核心技术。
HTTP Request/Response 结构.
正式的文档在这里RFC 2616 - Hypertext Transfer Protocol (HTTP/1.1).
Java Servlets.
开始学习的一个好地方是 Sun Servlet product page 和 Sun Java Tutorials.
JavaServer Pages (JSP).
类似的,一个开始学习的好地方是 Sun JSP product page 和Sun Java Tutorials.
JavaBeans.
很多Struts的类被编写成为Javabeans.如果您没有接触过Javabeans,参阅 Sun JavaBean product page 和 Sun Java Tutorials
如果您在其他平台上开发过web applications,您也许可以继续。在需要的时候参考以上的资料。他们几乎是所有Java web开发项目需要用到的核心技术。

1.1 前言 :回到从前!(也是Struts的简史)
当java servlets被发明出来的时候,很多程序员迅速意识到这是一个好东西。它比标准CGI快速强大,可以移植,并且几乎可以无限扩展。
但是无穷无尽的编写 println()语句来吧HTML送到浏览器是浪费时间并且容易造成问题。答案就是JavaServer Pages,它把编写servlet的任务倒了个个儿。现在开发者可以很容易的把HTML和Java代码混合起来,并且仍然拥有servlet的所有好处。只有天空才会限制(我们)!
Java web程序很快变成了以JSP为中心的。关于(JSP)本身并不是一件坏事,但是它并没有解决流程控制和web application的其他一些问题
显然,我们需要另一个模型...
很多聪明的程序员认识到JSP和servlets可以被一起使用来构造web程序。Servlets可以用来辅助流程控制,JSP主要关注于实现那些凌乱的HTML.在这样的过程中,一起使用JSP和servlet开始被称为Model 2.(同时只使用JSP被称为Model 1)。
当然,天底下没有新东西...很快很多人指出JSP的Model 2 遵循SmallTalk的经典的Model-View-controller设计模式。现在术语Model 2和MVC可以互相替代。
Struts工程于2000年5月由Craig R. McClanahan开始动工,来为Java社区实现一个标准的MVC框架。2001年6月,Struts 1.0发布了。从此,Model 2开发开始变得不同。

1.2 Model-View-Controller ('MVC') 设计模式
在MVC设计模式中,程序流程由一个的Controller(控制器)居中斡旋。Controller委派请求--在我们的情形中,HTTP请求--到合适的handle(处理程序)去。Handle和Model(模型)相联系,每一个handle的作用就是成为在请求和Model之间的适配器(adaptor有一个意思是电源插头--译者注)。Model 代表或者封装了一个程序的商业逻辑或者状态。通常控制由Controller重新转移到对应的View去。这个转移由一系列的映射决定,一般从数据库或者配置文件中取得。这样就为View和Model提供了一个松散的联系,编写和维护程序变得显而易见的的容易。

1.3 Struts 框架纵览
忠实的和Model-View-Controller设计模式对应,Struts程序有三个主要的部件:一个Servlet Controller,它由Struts本身提供,JSP 页面("view"),还有程序的商业逻辑("model").我们来看看他们是怎么组合到一起的。
Struts的Controller servlet把HTTP 请求打包并传送到框架中的其他对象,包括JavaServer Pages和由Struts开发者编写的org.apache.struts.action.Action的子类。当(系统)初始化的时候,Controller 解释一个配置资源文件。这个文件定义了(和其他配置一起)这个程序的org.apache.struts.action.ActionMapping。Controller用这些映射来把HTTP 请求转换为程序的actions(动作)。
一个ActionMapping通常会指出:
一个request请求的路径 (简称 "URI"),
用于处理请求的object type(对象) (Action 的子类),
和其他一些必要的属性。
Action 对象可以处理请求并响应客户端(通常是一个web browser),或者指出流程应该被指向另一个地方。例如,如果登录成功,一个login Action可能希望把流程转移到主菜单。
Action对象可以操作程序的Controller servlet,所以可以操作这个Controller的方法。当转发控制的时候,一个Action对象可以不直接的转发一个或多个共享对象,包括JavaBeans,这个过程是通过把它们放到Java servlet共享的某一个标准collection对象里去。
Action对象可以创建一个购物车Bean,在购物车里面加上一个条目,把这个bean放到session 结构里去,然后把控制转发到另一个映射。那个映射会使用一个JavaServer Page来显示用户的购物车里的内容。因为每个用户都有他自己的session,他们也会有自己的购物车。在一个Struts程序里,大部分商业逻辑都可以由JavaBeans表示。一个Action可以调用一个JavaBean的属性,不需要知道她实际上是如何工作的。这封装了 商业逻辑,所以Action可以专注于错误处理和如何转发控制。
JavaBeans也可以用来管理输入的Forms.设计一个web程序的主要问题是得到并检查用户在请求中的输入。通过Struts,你可以 定义你自己的Form Bean 类集合,他们都是org.apache.struts.action.ActionForm的子类,并很容易的把用户form(这里是指HTTP form--译者注)输入的内容放到这些Form bean里去。这个bean被存放到一个标准的共享上下文集合里去,所以另一个对象可以使用它,特别是指一个Action对象。 (我的理解,使用ActionForm是因为这样可以标准化的检查Form里的值,也可以标准化的调用Action--译者注。)
Form bean可以被一个JSP使用来从用户收集数据...通过一个Action 对象来检查用户输入的数据...然后再通过JSP来重新显示form的字段。如果检查错误,Struts由一个公用的机制来提交和显示错误信息。
Struts 的Form bean在配置文件中声明,在一个Java文件中被定义,并且通过一个通用的属性名字来连接到一个ActionMapping去。当一个请求 调用一个需要FormBean 的Action时候,Controller servlet要么从输入中获取form bean,要么自己构造一个form bean,把它传递到Action对象去。Action 对象可以在显示之前检查form bean的内容,也可以为要在form里显示的内容排队。准备好之后,Action对象返回控制权并转移到它的输入叶面去。controller就可以响应HTTP请求,并重定向到JavaServer Page去。
Struts 框架包括自定义的Tag来自动从一个formBean填充子段。JavaServer Page唯一需要知道的是确切的字段名和下一步提交form到何处。一些部件例如被Action排队的消息可以在一个自定义tag就被输 出。也可以定制其他为程序定制的tag来从jsp中隐藏实现细节。
在Struts框架中的自定义tag被设计为使用Java平台内置的国际化特性。所有的字段名和消息都可以从消息资源中获取,并且Java可以自动为一个客户端的国家和语言提供想对应的资源。为了提供另一种语言的支持,只需简单的的增加另一个资源文件。
除了国际化之外,这个进步的好处还在于在不同的form中保持一样的标签名字,也可以全局化的复查所有的标签和消息。
对于最简单的程序来说,有时候一个Action对象可以处理一个请求对应的商业逻辑。但是,在大多数情况下,一个Action对 象应该调用另一个对象,一般是一个JavaBean来处理实际的业务逻辑。这让Action专注于错误处理和流程控制,而非商业逻 辑。为了在其他平台上重用,商业逻辑JavaBean不应该引用任何web程序的对象。Action对象应该从HTTP请求翻译必要的细节并 象普通的java变量一样传递到商业逻辑对象去。
举个例子,在一个数据库程序中:
一个商业逻辑bean会连接到数据库并进行查询,
商业逻辑bean把结果传递到Action,
Action把结果保存在一个form bean中,放置到request中去,
JavaServer Page把结果显示到一个HTML Form中去。
不管是Action还是JSP都不需要知道结果是从哪儿来的。他们只需要知道如何打包和显示结果。
用户指南的其它部分非常详细的解释了Struts的不同部件。Struts发布版本也包含了一些开发者手册包含几个不同的主题,也包括例子程序,标准的Javadoc API,当然还有全部的源代码!
Struts 在Apache软件基金会协议下发布。代码拥有版权,但是可以在任何程序中自由的使用。参阅ASF license 得到详细信息.

1.4 Model(模型): 系统状态和商业逻辑JavaBeans
基于MVC的系统中的Model(模型)部分可以被分为两个概念,系统的内部状态和可以用来改变状态的动作。从语法上来说 ,我们可以把状态信息看成是名词,而动作是动词(用来改变状态)。
一般来说,米的程序需要用一套包含一个或多个JavaBeans来表达内部状态,(这些)JavaBean包含了这个状态的详细属性。根据 你的程序的复杂程度,他们可能是自我包含的(自己知道怎么样保留持续化信息),或者仅是一个知道必要时如何从外部数据源(如数据库)获取信息的对象。Entity Enterprise JavaBeans (Entity EJBs)一般也用于表示内部状态。
大型程序经常使用一些bean的方法来表示一组可能的商业逻辑,这些方法维护状态信息。例如,你可能有一个购物车Bean,保存 在每个用户的session中,包含用户当前决定购买的条目。这个bean可能有一个checkOut()方法来取得用户信用卡 的授权,发送订单到仓库去来发送。另一些系统可能把可能的动作单独包装,可能是一个Session Enterprise JavaBeans(Session EJBs).
在一些较小的程序中,相反,可能的动作会内置于Action类中,是Controller角色的一部分。当逻辑非常简单的 时候,或者当重用商业逻辑不是关注点的时候这是合适的。Struts框架支持任何一种模式,但是强烈建议把商业逻辑 (如何做)从Action类扮演的角色(做什么)中分开。

1.5 View(视图): JSP页面和表示部件
基于Struts程序的View部分基本上是由JavaServer pages(JSP)技术构成的。JSP页面可以包含静态的HTML(或者XML)文本,被称为“模版文本”,再加上在特殊的标签运行时(当页面被调用的时候)插入动态内容的能力。JSP环境包含一套标准的动作标签,比如<jsp:useBean>他们的 用处在JavaServer Pages Specification描述了。另外,还有一种标准的方法来定义你自己的标签,他们被组织在“自定义标签库”里。
Struts包含了一个内容广泛的自定义标签库,用以帮助编写完全国际化的用户界面。它也和处于Model部分的ActionForm高度互动。关于使用这些标签的方法在后面会详细讨论。
除了JSP 页面和原有的以及自定义的标签之外,有时候让商业逻辑对象拥有把他们自己根据当前的状态渲染成HTML(或者XML)的能力也 是经常需要的。这些被渲染出来的输出可以很容易的在一个结果JSP文件里面用<jsp:include>标准动作标 签引用。

1.6 Controller(控制器): ActionServlet 和 ActionMapping
程序的Controller(控制器)部分关注于从客户端接收请求(通常是一个使用浏览器的用户),决定那个商业逻辑方法应该被执行,然后分配任务给到一个合适的view组件来制造下一步的用户界面。在Struts里面,Controller的主要部分是一个servlet,它是ActionServlet.这个servlet用一组ActionMappings来配置,一个ActionMapping定义了一个path(路径),它和用户请求的URI对应,一般会指定一个完整的Action类的类名。所有的Action类都是org.apache.struts.action.Action的子类。Action封装了商业逻辑,组装结果,并且最终分配控制权到合适的View部件来创建结果。
Struts也支持在ActionMapping中使用除了标准的框架所需之外附加的属性。这样允许你保存你的程序所特定的附加信息。除此之外,Struts还允许你定义控制转移的逻辑“名字”,这样一个Action方法可以要求“主菜单”(举例)而不需要知道对应的JSP页面的实际名字。这个功能很大的帮助你把控制逻辑(做什么)和表示逻辑(如何显示)分开。
下一章: 构造Model(模型)部件


--------------------------------------------------------------------------------

Copyright (c) 2000-2002, Apache Software Foundation
 

--------------------------------------------------------------------------------

快速连接
Home
目录
简介
Model 部件
View 部件
Controller 部件
资源
我们是谁
2. 构造Model(模型)部件
2.1 纵览
很多用于构造web程序的需求文档都是关注于View(视图)的。但是,你需要确认每一个确定的请求需要的处理也是在Model(模型)层清楚的定义的。一般说来,Model(模型)的开发者会关注如何建立满足所有功能需求的JavaBean类。一个特定的程序所需要的明确定义的bean会有很大不同,取决于他们的需求。然而他们大致上可以被归为下面会讨论的几类。然而,先做一个关于“Scope范围”这个概念的简要的回顾会是很有用的。

2.2 JavaBeans 和 scope(范围)
在一个基于web的程序中,JavaBean可以被存放在(也是获取于)几个不同的“属性”集合。每一个集合有不同的关于集合的生存期和储存在其中的bean的可视范围的规则。定义生存期和可视范围的规则共同被称为这些bean的“scope(范围)”。JavaServer pages(JSP)的规格书定义了以下的术语(在servlet API中同样的也有这些定义):
page - Bean在一个单一的JSP页面中可见,生存期是这个请求。(service()函数的本地变量)
request - Beans 在一个单一的JSP页面中可见,包括被包含在这个页面中或者被此页面转移到的任何页面或者servlet. (Request 属性)
session - Beans 在一个特定的用户session中的所有JSP页面和Servlet中可见,跨越一个或多个请求。(Session 属性)
application - Beans 在web 程序的所有的JSP页面和servlet中可见。 (Servlet context属性)
记住同一个web程序中的JSP页面和servlet共享同样的bean集合是很重要的。例如,如下的代码在一个servlet中把一个bean加入了request的属性:
MyCart mycart = new MyCart(...);
request.setAttribute("cart", mycart);

马上在这个servlet转移到的JSP页面中可见,通过使用一个标准的动作tag,就像这样:
<jsp:useBean id="cart" scope="request"
class="com.mycompany.MyApp.MyCart"/>

2.3 ActionForm Beans
注意:ActionForm Beans实际上更靠近View(视图)而非Model(模型)。
Struts框架一般会假设你已经为你程序中每一个需要输入的Form定义好了一个ActionFormBean(这是一个从ActionForm继承的一个类).ActionForm beans有时候就叫做"form beans"。如果你已经在你的ActionMapping配置文件里(参阅"构造Controller(控制器)部件")生命了这些bean, Struts Controller servlet在调用合适的Action方法之前会自动为你提供如下服务:
在用户的session里面察看在恰当的键值下是否有一个合适的类的实例。
如果没有这样的session范围的bean存在,会自动创建一个新的实例并加入到user的session中。
对每一个request 参数,如果能对应于(form) bean的一个同名属性,会调用相应的setter方法。这个操作很象标准的JSP动作<jsp:setProperty>,当你用'*'符号来选择所有的属性的时候。
更新过的ActionFormbean 会被传递到Action 类的perform()方法,(当这个方法被调用时)这些值马上就可以使用了。
当你编写你的 ActionForm bean的时候,记住这些基本的原则:
ActionForm类本身不需要实现具体的方法。它是用来为了明确特殊的bean在整个体系中的角色的。一般来说,一个ActionFormbean 只需要属性的getter和属性的setter方法,没有商业逻辑。
ActionForm 对象也提供了一个标准的检查机制。如果你重载了一个"stud"函数,并且在标准的程序资源里给出了错误信息,Struts会自动检查输入的值(使用你的方法)。参阅"Action Form 检查" 得到详细说明。当然,你可以忽略在ActionForm中的检查而在你的Action对象中自己进行。
为每一个form中出现的字段定义一个属性(附带相应的getXxx()和setXxx()方法)。字段的命名和属性的命名必须遵循通常的JavaBeans规范。举例来说,一个输入的字段名叫username会导致setUsername()方法被调用。
设想你的ActionForm bean是HTTP和Action之间的防火墙。用validate方法来保证所有需要的书形式存在的,并且包含合理的值。一个没有通过检查的ActionForm不会被送到Action去处理。
你也可以在你的form里面放置一个bean的实例,来使用内置的属性引用。比如,你可能在你的ActionForm上有一个“customer”bean,可以在你的JSP 中使用属性"customer.name"。这会对应到你的customer bean的customer.getName()和customer.setName()方法。 参阅 tag Library Develiper Guides 得到更多关于内置属性语法的说明。
警告: 如果你在你的form上放置一个以存在的bean的实例,考虑它暴露出来的属性。任何public的,接受一个单一的字符串的属性可能被一个查询字符串重置。这些bean放在一个瘦的“包装者”类里面,只暴露需要的属性可能时有所帮助的。这个包装者也可以提供一次过滤来保证运行时的属性不被置为无意义的值。
你可能注意到了一个在这里讨论的"form",并非必须是用户界面端一个单独的JSP页面。在很多程序里都会出现跨越多个页面的"form"(从使用者的角度来说)。想一下,举例来说,在安装新程序时经常会出现的向导风格的用户界面。Struts鼓励你定义一个单独的ActionForm包含所有可能用到的属性,不管到底是在那个页面显示的。同样的,同一个form的不同页面应该被提交到同样的Action类。如果你遵循这些建议,页面设计者可以重新在页面之间安排这些字段,通常不需要修改处理逻辑。

2.4 系统状态Beans
系统的实际状态一般会表现为一套包括一个或多个JavaBean类,它们的属性定义了当前的状态。一个购物车,举例来说,会包括为每一个购物者维护一个bean,并且会(和其他属性一起)包括一些购物者当前选择的物品条目。另外,系统也会包括不同的bean来保存用户的个人资料(包括他们的信用卡号码和送货地址),还有按照他们的购物等级所能得到的可购买物品清单。
对于一个小型的系统,或者对不需要长期保存状态信息的系统来说,一套系统状态bean可能已经保存了系统所需要的全部特定细节。否则,大多数情况下,系统状态bean会把它持有的信息包村到一个外部的数据库去(比如一个CustomerBean对象对应于CUSTOMERS表中的一个特定的行),并可以在服务器的内存中按照需要创建或者删除。Entity Enterprise JavaBeans 在大规模系统也是为了这个目的使用的。

2.5 商业逻辑 Beans
你应该把你的程序的功能逻辑封装在你为此设计的JavaBean的方法调用中。这些方法可能是同一个系统状态bean,或者他们分布在不同的类中,潜在的体现了逻辑。在后一种状况中,你往往需要把系统状态bean作为参数调用以便这些方法处理。
为了最大化代码重用,商业逻辑bean因为被设计和实现为假设它们不知道会在web环境下被执行。如果你发现你在你的bean中importjavax.servlet.*,你(就知道你在)在试图把这个商业逻辑bean放置在web环境中。请考虑重新编排,让你的Action类(属于controller角色的一部分,下文将会谈到)从HTTP请求翻译所有需要的信息,通过调用你的商业逻辑bean的setter方法预置,然后调用一个execute()方法来执行。这样的商业逻辑类可以在不是最初的Web的环境下重用。
取决于你的程序的复杂程度和范围(宽广程度),商业逻辑bean可能是普通的JavaBeans(它们和作为参数传递的系统状态bean打交道),或者是通过JDBC调用存取数据库的一般的JavaBeans。对于大规模程序来说,它们往往是被有状态的或者无状态的Enterprise JavaBeans(EJBs)所取代。

2.6 访问关系数据库
Struts可以在它的标准配置文件中为程序定义datasource.也提供一个简单的JDBC连接池。参阅Action Mappings 配置文件和Utilities Developer Guide(工具开发指南)得到更详细的信息。
当datasource被定义号之后,下面是一个在Action的perform方法中取得连接的例子。
public ActionForward
perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
{
try {
javax.sql.DataSource dataSource =
servlet.findDataSource(null);
java.sql.Connection myConnection =
dataSource.getConnection();
//do what you wish with myConnection
} catch (SQLException sqle) {
getServlet().log("Connection.process", sqle);
} finally {
//enclose this in a finally block to make
//sure the connection is closed
try {
myConnection.close();
} catch (SQLException e) {
getServlet().log("Connection.close", e);
}
}
}
注意Struts 普通连接池是一个可选组件。很多Struts程序使用其它连接池来获取更高的性能,特别是大流量的生产系统。
下一章: 构造 View (视图) 部件


--------------------------------------------------------------------------------

Copyright (c) 2000-2002, Apache Software Foundation
 

--------------------------------------------------------------------------------

快速连接
Home
目录
简介
Model 部件
View 部件
Controller 部件
资源
我们是谁
3. 构造 View (视图)部件
3.1 纵览
这一章关注构造程序的View(视图)部件的任务,主要是由JavaServer Pages(JSP)技术完成的。特别的,Struts提供对编写国际化程序的支持,同时也支持和(用户)输入的Form交互。其他几个和View(视图)有关的主题也会被简要的涉及。

3.2 Internationalized Messages
几年前,程序开发者们只期望支持他们自己国家的国民,只使用一种(有时候是两种)语言,也只有一种表达日期,数字,钱币的格式。然而,基于Web技术的,在internet和其他一些可以被广泛访问的网络上的程序开发有了爆炸性的增长,让国界问题浮现在我们的眼中。这个问题被理解为对于支持国际化(经常被称为i18n,因为在"国际化"一词internationalization中i和n之间有18个字母)和本地化程序的需要。
Struts 在Java平台上编写,可以辅助支持编写国际化和本地化的程序。需要熟悉的概念有:
Locale (国籍,地区) - 支持国际化的最基本的Java类是java.util.Locale。每一个Locale表示一个特定的国家和语言(包括一种可选的不同语言),和一套对于数字,日期之类的字符串的格式化方案。
ResourceBundle (资源包) - java.util.ResourceBundle类提供了支持不同语言的信息的基本支持。参阅ResourceBundle的Javadoc和在你的JDK版本中的国际化支持的信息来得到进一步的帮助。
PropertyResourceBundle (属性资源包) - 一个ResourceBundle的标准实现,它允许你用和properties文件一样的"name=value"的格式来定义资源。这对于在web程序中准备信息资源宝石很方便的,因为大部分的信息都是文本。
MessageFormat (信息格式) - java.text.MessageFormat 类允许你在运行时用参数的方式替代一个信息字符串(在这种情况下,是从reousece bundle中取出的)。有时候你要编写一个句子,但是在不同的语言中词语出现的顺序不一样,这就很有用了。字符串中的占位符{0}会被第一个运行时参数取代,{1}会被第二个取代,其它的也是这样。
MessageResources (信息资源) - Struts类org.apache.struts.util.MessageResources让你把一套resource bundle当成数据库,允许你得到某种特定的Locale(一般是当前用户的Locale)中的特定信息字符串,而非服务器自己正在使用的Locale。
请注意在一个Struts之类的框架中i18n的支持只限于显示国际化的字符串和图像给用户。支持地区相关的输入法(在日语,中文,或者韩文之类的语言中使用)被留给客户端设备,一般是一个web浏览器。
对于一个国际化程序来说,请遵循JDK中附带的国际化文档中描述的步骤来为你的平台创建每一种语言的properties文件。下面的例子更深入的说明这一点:
假设你的源代码位于com.mycompany.mypackage包,所以它保存在com/mycompany/mypackage目录中(相对于你的源代码目录)。要创建一个名为com.mycompany.mypackage.MyResources的资源包,你应该在com/mycompany/mypackage下创建下列文件:
MyResources.properties - 包含了使用你的服务器所在的语言的文本。如果你的默认语言是英语,你可能会有如下的条目: prompt.hello=Hello
MyResources_xx.properties - 包含了ISO代码为"xx"的语言的文本(参阅ResourceBundle的Javadoc得到最新的代码列表)。对于一个上面所示文本的法语版本,你可能会有如下的条目: prompt.hello=Bonjour 你可以编写所以你愿意支持的语言的资源包。
当你配置你web程序中controller servlet的发布描述时,你需要定义的一个国际化参数是程序的基本资源包的名字。在我们上面举的例子中,它就是com.mycompany.mypackage.MyResources。
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>application</param-name>
<param-value>com.mycompany.mypackage.MyResources</param-value>
</init-param>
<.../>
</servlet>
一件重要的事情是要让你的资源包在你程序可以访问的class path中。另一个方法是把MyResources.properties放在你的程序的classes目录下,你就可以简单的用“myResource”作为程序的值。但是要注意如果你的编译脚本为了保证一个“干净”的编译目标,会删除整个classes目录,这时要保证这个资源文件不被删除。
如果是这种情形,这儿有一段Ant任务可以包含在你的编译脚本中,它把src/conf目录中的内容拷贝到classes目录去。
<!-- Copy any configuration files -->
<copy todir="classes">
<fileset dir="src/conf"/>
</copy>


3.3 Forms 和 FormBean 的交互
以前,大多数web开发者使用标准的HTML能力来编写Forms,比如使用<input>标签。用户希望和程序互动,产生某种特定的行为,其中之一和错误处理有关——如果用户犯了个错误,程序应该允许他们改正需要改正的地方——而非重新填写页面上所有的字段。
使用标准的HTML和JSP页面来满足这种需求是非常繁琐和粗笨的,举个例子,一个username字段的输入框可能是这样的(在JSP里):
<input type="text" name="username"
value="<%= loginBean.getUsername() %>"/>
如果HTML程序员并没有编程基础知识的话,这很难输入正确,而且也会对页面编辑者带来麻烦。基于JSP1.1中的自定义标签库,Struts为编写form提供了很大的方便,可以取代原始的方法。上面的例子用Struts来编写的话会是这样:
<html:text property="username"/>
并不需要显式的引用一个JavaBean以取得初始值。框架自动完成这些事情。
HTML form有时候被用来上传一些文件。大部分浏览器通过<input type="file"> 元素来支持这一点,它会显示一个浏览文件的按钮,但是(在Server端--译者注)下面就轮到开发者来处理这些(上传)来的文件了。Struts可以用和普通的form一致的方式来处理这些"multipart" form.在下一节里,我们会谈到用Struts来创建一个简单的login页面,还包括一个简单的multipart form。

3.3.1 在Struts里编写Form
一个完整的login页面有助于展示Struts如何用比直接的HTML和标准JSP方式更轻松的方式来处理form。考虑如下的页面(基于Struts附带的示例程序),名字是logon.jsp:

--------------------------------------------------------------------------------
<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-html.tld"
prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld"
prefix="bean" %>
<html:html>
<head>
<title>
<bean:message key="logon.title"/>
</title>
<body bgcolor="white">
<html:errors/>
<html:form action="/logon" focus="username">
<table border="0" width="100%">
<tr>
<th align="right">
<html:message key="prompt.username"/>
</th>
<td align="left">
<html:text property="username"
size="16"/>
</td>
</tr>
<tr>
<th align="right">
<html:message key="prompt.password"/>
</th>
<td align="left">
<html:password property="password"
size="16"/>
</td>
</tr>
<tr>
<td align="right">
<html:submit>
<bean:message key="button.submit"/>
</html:submit>
</td>
<td align="right">
<html:reset>
<bean:message key="button.reset"/>
</html:reset>
</td>
</tr>
</table>
</html:form>
</body>
</html:html>

--------------------------------------------------------------------------------
根据上面的例子,下面描述了Struts里form处理的关键特性:
taglib指令告诉JSP页面编译器如何去寻找关于Struts标签库的标签库描述。在这里,我们用bean作为表示struts-bean标签库的前缀代号,而"html"是struts-html的前缀代号。你可以用任何你希望使用的前缀。
这个页面使用了几个message 来从包含这个程序所有可用资源的MessageResources对象中查找国际化信息串。下列文本关键字必须在资源文件中定义,这个页面才能正常工作:
logon.title - logon 页面的标题
prompt.username - "Username:" 询问用户名的标签
prompt.password - "Password:" 询问密码的标签
button.submit - "Submit" "提交表单"的按钮的标签
button.reset - "Reset" "重置"按钮的标签
当用户登录的时候,程序会在用户的session中保存一个Locale对象。这个Locale对象会被用来选择合适的语言信息。这样让用户自己选择语言会变得很容易——简单的改变这个保存的Locale对象,所有的文本都会自动切换。
错误标签显示任何保存在商业逻辑部件中的可能的错误信息,如果没有错误,那么就不显示。关于这个标签,下面会详细讨论。
根据指定的属性,form标签展开为一个HTML <form>元素。它也让所有在这个form内部的字段值被存放到一个session范围的FormBean中去,它在session中的关键字是logonForm。Struts开发者会给出关于这个form bean的Java实现,它是一个Struts类ActionForm的字类。这个bean用来给出所有的输入字段的初始值,只要字段名和bean的属性名一致。如果合适的bean没有找到,会自动用指定的Java类名字来创建一个新的。
这个formbean也需要在Struts配置文件中指明。这里名字和类型可以被省略。参阅"Action Mappings (动作映射)配置文件"得到详细信息。
Text标签展开成为HTML里的<input>元素,它的类型是"text"。在这里,在浏览器中显示时占据多少个字符空间也被指明了。当这个页面被执行的时候,对应的bean的username属性的值会被作为初始值(就是getUsername()的返回值)。
password 标签用起来很简单。不同之处是当用户输入自己的密码时浏览器会显示星号而非用户输入值。.
submit和reset标签生成form中对应的按钮。其上的标签文字是用message 标签生成的,所以它们显示的是国际化的字符串。
处理multipart form也是很简单的。显而易见,当你编写一个multipart form的时候你创建的form中具有至少一个"file"类型的input输入框。编写一个multipart的form的第一步是使用struts-html标签库来创建显示页面:

--------------------------------------------------------------------------------
<%@page language="java">
<%@taglib uri="/WEB-INF/struts-html.tld"
prefix="html">
<html:form action="uploadAction.do">
Please Input Text:
<html:text property="myText"><br/>
Please Input The File You Wish to Upload:<br/>
<html:file property="myFile"><br />
<html:submit />
</html:form>

--------------------------------------------------------------------------------
下一步是创建你的ActionForm bean:

--------------------------------------------------------------------------------
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.upload.FormFile;
public class UploadForm extends ActionForm {
protected String myText;
protected FormFile myFile;
public void setMyText(String text) {
myText = text;
}
public String getMyText() {
return myText;
}
public void setMyFile(FormFile file) {
myFile = file;
}
public FormFile getMyFile() {
return myFile;
}
}

--------------------------------------------------------------------------------
在Javadocs里查阅FormFile可以看到在文件上传时它提供的处理文件的方法。也要看Javadoc里面的ActionServlet 和ActionMapping的不同的参数,你可以指定它们来指明文件如何上传。基本上你在你的Action类的perform()方法中需要调用((UploadForm) form).getMyFile()得到你的文件并且做出你的处理。

3.3.2 支持的Input 字段的类型
下面列出了Struts定义的所有的input字段的类型,带有指向手册的连接。
checkboxes 选择框
hidden fields 隐藏字段
password 密码输入框
radio 单选框
reset 重置按钮
select 列出内值的选择条目(下拉选择框)
option备选条目
options备选条目
submit 提交按钮
text 单行文本输入框
textareas 多行文本输入框
在任何情况下,字段标签都必须内置在form标签中,这样这些字段才知道用哪个bean来得到初始值。

3.3.3 其它有用的表示层标签
有好几个有用的tag用来创建表示层,请查阅它们的标签库的文档和Tag Develipers Guide得到更多的信息:
[逻辑] iterate 枚举 为一个列表中的所有元素重复输出标签。(这个列表可能是 Enumeration,Hashtable, Vector, 或者对象数组)。
[逻辑] present 存在 根据一个指定的属性,这个tag在当前的request中检查,如果这个属性存在的话就显示它所包含的页面内容。每次使用这个标签的时候只能指定一个属性,名字是必需的。属性可以包括cookie, header,name,parameter,property,role,scopr还有user。
[逻辑] notPresent 不存在 和present作用相反的标签,当指定的属性不存在的时候显示。
代码:
 link 连接 创建一个 <a> HTML元素,作为一个锚点或者指向指定URL的超链接,它会自动进行URL编码,以在不支持cookie的时候维护session状态。 
[html] img 图像 创建一个 <img> HTML元素,它可以动态改变"src"和"lowurl"中指定的URL,和<html:link>的方式相同。 
[bean] parameter 参数 获取特定的request 参数的值,把结果作为一个page范围的属性,类型是字符串或者字符串数组。 
 
3.3.4 自动form检查 
除了上文提到的form和bean之间的交互之外,Struts还提供了附加的机制来检查它接收到的输入字段。要使用这个特性,在你的ActionForm中重载下面的这个类: 
public ActionErrors
  validate(ActionMapping mapping,
    HttpServletRequest request);
validate() 方法在bean的属性被自动填充之后,在对应的Action类的porform()方法调用之前由controller servlet自动调用。validate()方法有下面几个选择: 
进行了恰当的检查,没有发现任何问题——返回null或者长度为零的ActionError实例,Controller servlet会继续调用对应的Action类的的perform()方法。 
进行了恰当的检查,发现了问题。——返回一个包含ActionError的ActionErrors实例,它包含了应该显示错误信息的关键字(位于程序的MessageResources 信息资源库中的关键字)。controller servlet会把这个数组保存为request的属性,以便<html:errors>标签使用,并且流程会重新转回到输入form(通过ActionMapping的input属性得到)。 
前面曾经提到过,这个特性完全是可选的。默认的validate()方法返回null,controller servelt会认为对应的Action类会做必要的检查。 
另一个常用的方式是在ActionForm的validate()方法中进行简单的主要的检查,在Action里面做“商业逻辑”检查。 
在夜间编译包和下列地址还有一个可选的ActionForm检查的包可以使用:David Winterfeldt的网站。 
 
3.4 其他表示层技术 
虽然你的程序的观感可以完全用标准的JSP和Struts的自定义标签库创建,你也许会考虑动用其他技术来增加部件重用,减少维护工作,并且/或者减少错误。下面几节讨论了几种可选项。 
 
3.4.1 针对程序的自定义标签 
除了Struts定义的自定义标签,你针对你正在编写的程序创建标签也很容易,这会帮助你编写用户界面。通过针对下面的程序编写tag,Struts附带的示例程序描绘了基本原理: 
checkLogon - 检察是否存在一个特定的session对象,并且如果没有的话就转向到登录页面。这可以防止用户在使用你的程序到一半的时候,把页面用书签记录下来,试图以后跳过登录这一步。或者防止session失效。 
linkSubscription - 生成一个指向明细页面的超连接,它传递需要的主键的值作为request属性。这在列出用户拥有的订阅纪录时有用,它提供了编辑或者删除订阅信息的链接。 
linkUser - 生成一个指向用户详细信息页面的超链接,把需要的主键信息作为request属性传递。 
这些tag的源代码都在src/example目录下,位于org.apache.struts.example包中,和其它在程序中需要的java类在一起。 
 
3.4.2 用Include(引用)来组合页面 
用一个JSP页面(包括子定义标签和Bean来取得必要的动态数据)在生成整个表现是一个很普通的方式,在Struts的示例程序中也出现过。然而,很多程序需要在同一个页面中显示多个不同的单一逻辑部分的组合。 
举例,一个门户站点可能在门户的主页上有以下几个功能部分: 
显示门户的搜索引擎 
一个或多个"新闻"显示部分,根据用户登记的个性文件中的兴趣组合。 
显示关于这个门户的讨论主题 
一个“有邮件在等待”提示,如果你的门户提供免费邮件账户。 
如果你能把工作分成不同的部分,为每个部分分配不同的开发者,开发工作会容易些。那么你需要用JavaServer Pages技术中的include(包含)能力,或者Struts提供的include 标签来把不同的页面结果组合到一起去。取决于你需要在什么时候组合你的输出,有三种不同的include可供选择: 
一个 <%@ include file="xxxxx" %>指令可以包含一个包括Java代码或者JSP标记的页面。其中的代码甚至可以访问父页面之前定义的变量。在JavaServer Page编译之前,这段代码已经被包含了,所以它可以包含的不只是HTML代码。 
include action(动作,这里指JSP中的标准指令)(<jsp:include page="xxxxx" flush="true" />)在请求时被处理,服务器会透明的处理它。除此以外,这意味着你通过在一个类似equals这样使用参数的标签中编写这条请求,可以有条件的进行包含。 
bean:include读取一个"forward"参数通过一个逻辑名字映射到一个被包含的jsp页面,或者读取"id"参数,打印一个页面上下文的字符串变量到jsp页面上。 
另一个方法是使用Struts Template Tag library(模版标签库)。察看Developer's Guide得到详细信息。 
Tiles是一个原来的Template Tag library的替代品,提供几处增强和新功能。Tiles位于夜间编译包,或者在Cedric Dumoulin的网站。 
 
3.4.3 图像生成部件 
有一些程序需要动态生成图像,比如价格图或者股票报告等等。一般有两种不同的方法满足这个需求: 
显示一个指向servlet请求的超链接。这个servlet会使用一个图像库来渲染出图像,把输出改为合适的内容类型(比如image/gif),并送出图像的字节流到浏览器。浏览器会像一个静态图像一样显示它。 
生成必要的HTML代码来下载一个Java applet来创建需要的图像。通过在生成的代码中传送初始化参数,你可以决定图像。或者applet也可以自行和server连结以取得这些参数。 
 
3.4.4 生成文字 
有些程序需要动态生成文本或者打包的文本,例如XML.如果整个页面都是被生成的,可以用PrintWriter输出,从Action里可以很容易的办到: 
           response.setContentType("text/plain");
// or text/xml
           PrintWriter writer = response.getWriter();
           // use writer to render text
           return(null);
         
下一章: 构造 Controller (控制器)部件 
 
 
--------------------------------------------------------------------------------
 
Copyright (c) 2000-2002, Apache Software Foundation
 

--------------------------------------------------------------------------------

快速连接
Home
目录
简介
Model 部件
View 部件
Controller 部件
资源
我们是谁
4. 构造 Controller (控制器)部件
4.1 纵览
现在我们已经理解如何构造你的程序的Model(模型)和View(视图)部件,现在是时候来关注Controllder部件了。Struts包含一个主要功能是映射一个请求URI到一个Action类的servlet。因此,对于Controller你的主要责任是关于:
为每一个可能接收的逻辑请求编写一个Action类(扩展自org.apache.action.Action)。
为每一个可能提交的逻辑请求在XML中配置一个ActionMapping。这个XML配置文件通常命名为struts-config.xml。
更新你的web程序的部署描述文件(在XML里)来包含需要的Struts部件。
在你的程序里增加恰当的Struts部件。

4.2 Action 类
Action类提供了两个可以被继承的方法,适应你的servlet环境:
public ActionForward perform(ActionMapping mapping,
ActionForm form,
ServletRequest request,
ServletResponse response)
throws IOException, ServletException;
public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException;
大部分工程可能只需要用到"HttpServletRequest"版本。
Action类的目标是通过它的perform()方法处理一个请求,返回一个ActinForward对象表明控制权应该被转移到哪里(例如一个JSP)作为合适的相应。在MVC/Model 2设计模式中,一个典型的Action类会在perform()中执行如下逻辑:
检查当前用户session的状态(比如,检查用户是否已经成功登录)。如果Action类发现尚未登录,请求可以被转移到一个JSP页面提示让用户输入用户名和密码。当用户试图访问一个程序的“中间部分”(可能是从一个书签)的时候,或者是session已经超时,servlet容器创建了一个新的session的时候者都可能发生。
如果检查还未完成的话,继续检查form bean的属性。如果发现了问题,把相应的错误的信息关键字放在request的属性里,然后把控制权重新转到输入页面来让用户改正。
进行请求需要的操作(比如在数据库中保存一行数据)。这可能由Action内部的逻辑代码自己来完成,但是一般应该调用一个商业逻辑bean的合适的方法。
更新服务器端的对象,来创建下一步用户面对的界面(典型的是request范围或者session范围的bean,取决于你需要这些条目保存多长时间)。
返回一个合适的ActionForward对象来挑选JSP页面根据刚刚更新的bean来生成回应。典型的,你需要通过你接收到的ActionMapping的findForward()方法或者从controller servlet自身(如果你使用一个全局的逻辑名字)来得到这样的一个引用。
在编写Action类代码时请记住以下几个设计要点:
Controller servlet只创建你的Action类的一个实例,用来对对付所有的请求。所以,你需要处理好你的代码,在一个多线程环境下你的Action应该运作良好,就象你编写一个servlet的service()方法一样。
最有助于编写线程安全的程序的原则是在你的Action类中只使用本地变量(local variable),而非实例变量(instance variable)。本地变量在堆栈中创建并依附于每一个独立的请求线程(由你的JVM处理),所以你不用担心共享它们。
你系统中表示Model(模型)的bean由可能因为访问数据库或者其他资源而抛出异常。你应该在你的perform()逻辑中捕获所有的异常,把它们记录到程序的log文件中去(同时给出对应的stack trace)。可以这样做:
servlet.log("Error message text", exception);
作为一项基本的规则,分配稀少的资源并且为每一个用户在它们的session里分配一个会导致规模问题。你应该在把你的控制转移到View(视图)部件之前就释放它们(比如数据库连接)——就算你调用的bean的方法抛出了异常也要这样。
除此之外,你还需要警惕你的Action类们变得太大。出现这种情况最容易的可能就是把功能逻辑放在Action本身,而不是在独立的商业逻辑bean。除了让Action类变得难以阅读和维护,这种方法还会让重用商业逻辑变得困难,因为它们被嵌入在部件内部(那个Action类),从而被限制在一个web程序环境中运行。
只要需要的所有属性都是在方法签名中传递的(指的是通过函数调用传递?——译者注),一个Action可以被分散在几个本地方法中。JVM把这些属性在堆栈中处理,所以它们就是是线程安全的。
Struts附带的例子程序只是设计用于展示某些原理,因为它自己的商业逻辑是被嵌入在Action类中的。这应该被理解为设计中的某种bug,而非Struts体系本身的特性,或者应该被模仿的方法。

4.3 ActionMapping 的实现
为了能成功的操作,关于如何把每一个请求URI映射到恰当的Action类,Struts controller servlet需要知道几件事情。这些需要的知识被封装在一个叫做ActionMapping的Java接口中,下列是最重要的几个属性:
type - 在这个映射中Action实现的完整java类名。
name - 这个action会用到的form bean在配置文件中使用的名字
path - 请求的URL用以选择(触发)这个映射。下面会有关于匹配如何工作的例子。
unknown - 当无法找到合适的其他action来处理请求时,某个action可以被配置为默认处理这个请求,这时候把这一项置为true。一个程序中只能有一个action作为默认处理action。
validate - 如果这个映射中涉及的action需要在调用前先调用validate()方法,把这一项置为true。
forward - 当这个映射被调用的时候,转向一个请求URI。这是type属性的一个替代品。

4.4 Action Mappings 配置文件
controller servlet是如何知道你希望的映射的?写一个小java类来创建新的ActionMapping的实例,并且调用每一个setter方法来赋值是可行的,但是非常繁琐。为了简化这个步骤,Struts包含一个Digester模块,它能够读取一段基于XML的关于需要的映射的描述,并创建合适的对象。参阅API 文档得到关于Digester的更多信息。
开发者的责任是编写一个命名为struts-config.xml的XML文件,并把它放在你的程序的WEB-INF目录下。文档的格式由"struts-config_1_0.dtd"中的描述指定。最外层的XML元素必须是<struts-config>。
在<struts-config>元素之内,有两个重要的元素被用于描述你的action:
<form-beans>
这一段包含你的form bean的定义。你为每一个form bean使用一个<form-bean> 元素,包含下列属性:
name: 这个bean的一个唯一的标识名,它将会被用来在action映射中引用来指定这个bean.通常,这也是把这个对象存放在request或者session中的属性的关键字。
type: 你的form bean的完整的Java类名。
<action-mappings>
这一段包含你的action的定义。你为你的每一个需要定义的action使用一个<action> 元素。每一个action元素需要定义下列的属性:
path: 这个action的程序上下文相关的路径
type: 这个Action类的完整类名
name: Action中所用到的<form bean>元素的名字
示例程序中的struts-config.xml文件包含了下列的映射元素,它们是用来实现“登录”功能的,我们用它来说明需求。注意其它的Action的条目都被剔除了:
<struts-config>
<form-beans>
<form-bean
name="logonForm"
type="org.apache.struts.example.LogonForm" />
</form-beans>
<global-forwards
type="org.apache.struts.action.ActionForward" />
<forward name="logon" path="/logon.jsp"
redirect="false" />
</global-forwards>
<action-mappings>
<action
path="/logon"
type="org.apache.struts.example.LogonAction"
name="logonForm"
scope="request"
input="/logon.jsp"
unknown="false"
validate="true" />
</action-mappings>
</struts-config>
首先定义的是form bean .一个基础的"org.apache.struts.example.LogonForm"类被映射为逻辑名"logonForm"。这个名字也被用在session或者request作为这个form bean的属性名。
"global-forwards"部分用来创建全局可用的逻辑名映射。这儿的任何一个转移都可以通过调用你的action mapping的实例实现,比如actionMappingInstace.findForward("logicalName")。
你可以看到,这个映射匹配/logon路径(实际上,因为实例程序用了后缀名映射,你指定的URI请求会是由/logon.do结尾的)。当收到一个匹配这个路径的请求时,会创建一个LogonAction的实例(仅当第一次),并且使用它。controller serverl会寻找一个session范围的名为logonForm的bean,如果需要的话,创建并保存这样的一个bean。
局部的"forward" 元素是可选的但是非常实用。在实例程序中,很多action包含一个局部的"success"和/或"failure"转移作为Action mapping的一部分。
<!-- Edit mail subscription -->
<action path="/editSubscription"
type="org.apache.struts.example.EditSubscriptionAction"
name="subscriptionForm"
scope="request"
validate="false">
<forward name="failure" path="/mainMenu.jsp"/>
<forward name="success" path="/subscription.jsp"/>
</action>
只用了两个额外的属性,示例程序中的Action类变得几乎和页面设计者用的实际JSP页面的名字无关了。页面可以在重新设计的时候被改名(举个例子),只会给Action类带来微不足道的冲击。如果“下一个”JSP页面的名字是被硬编码在Action里面的话,所有的类就必须做出改动。当然,你可以决定在你的程序中使用怎样的局部转移属性。
另一个很有用的部分是<data-sources>,它定义了你的程序可以使用的数据源。下面是你如何在你的程序的struts-config.xml中指定一个基本的数据源:
<struts-config>
<data-sources>
<data-source
autoCommit="false"
description="Example Data Source Description"
driverClass="org.postgresql.Driver"
maxCount="4"
minCount="2"
password="mypassword"
url="jdbc:postgresql://localhost/mydatabase"
user="myusername"/>
</data-sources>
</struts-config>
关于如何获取这个数据源,参阅访问关系数据库这一节。

4.5 Web 程序发布描述
设置你的程序的最后一步是配置程序的发布描述(保存在WEB-INF/web.xml文件中)来包含所有需要的Struts部件。用示例程序中的发布描述作为一个指南,我们发现下列条目需要创建或者修改。
4.5.1 配置Action servlet实例
增加一个条目来定义action servlet自己,包含合适的初始化参数。这样的一个条目可能是这样的:
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>
org.apache.struts.action.ActionServlet
</servlet-class>
<init-param>
<param-name>application</param-name>
<param-value>
org.apache.struts.example.ApplicationResources
</param-value>
</init-param>
<init-param>
<param-name>config</param-name>
<param-value>
/WEB-INF/struts-config.xml
</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>mapping</param-name>
<param-value>
org.apache.struts.example.ApplicationMapping
</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
controller servlet支持的初始化参数如下所示。(你也可以在Javadocs中查找ActionServlet类得到详细信息。)方括号表示如果你没有给出初始值时使用的默认值。
application - 程序的资源包的基础类的Java类名。 [NONE]
bufferSize - 处理文件上传的输入缓冲区大小。[4096]
config - 指向包含我们的配置信息的XML资源的相对于上下文的路径。[/WEB-INF/struts-config.xml]
content - 默认输出响应的的文档类性和字符集编码。它可以被一个转移到的servlet或者JSP重载。[text/html]
debug - 这个servlet的debug信息详细程度,它控制多少信息应该被记录。[0]
detail - 我们在initMapping()中使用的Digester的debug信息详细程度,它输出到System.out而非servlet的log。[0]
factory - 用于创建程序的MessageResources对象的MessageResourcesFactory的类名。 [org.apache.struts.util.PropertyMessageResourcesFactory]
formBean - ActionFormBean继承的根的Java类名。[org.apache.struts.action.ActionFormBean].
forward - ActionForwarder继承的根的Java类名。 [org.apache.struts.action.ActionForward]. 两个你可能选择的类是: Two convenient classes you may wish to use are:
org.apache.struts.action.ForwardingActionForward - org.apache.struts.action.ActionForward的子类,它的 redirect属性被默认置为false(和Actionforward 默认值一样)。
org.apache.struts.action.RedirectingActionForward - org.apache.struts.action.ActionForward的子类,它的redirect属性被默认置为true。
locale - 如果被置为 true, 并且有一个用户session,如果没有Locale对象的话,放置一个合适的java.util.Locale object (用Action.LOCALE_KEY作为标识关键字)到用户session中去。[true]
mapping - ActionMapping继承的根的Java类名。[org.apache.struts.action.ActionMapping]. 两个你可能选择的类是:
org.apache.struts.action.RequestActionMapping - org.apache.struts.action.ActionMapping 的子类,scope属性默认是"request"。
org.apache.struts.action.SessionActionMapping - of org.apache.struts.action.ActionMapping 的子类,scope属性默认是"session"(和ActionMapping默认值一样)。
maxFileSize - 一次上传文件可能的最大大小(单位是字节)。可以在数字后面加上"K","M',或者"G", 代表千字节,兆字节和千兆字节。[250M]
multipartClass - 用于文件上传的MultipartRequestHandler 的实现的完整Java类名。[org.apache.struts.upload.DiskMultipartRequestHandler]
nocache - 如果设置为true, 在每一个响应中加上HTTP头信息来访置我们的任何响应或者转移被浏览器缓存。[false]
null - 如果设置为true, 如果(资源)信息的关键字无法找到,返回一个null。否则,返回一个带有此关键字的错误信息。[true]
tempDir - 文件上传时的临时目录。 [这个web程序的servlet上下文属性指定的工作目录]
validate - 我们是否适用性的配置文件格式?[true]
validating - 我们是否使用一个带有校验的XML 解释器来处理配置文件(强烈建议)? [true]

4.5.2 配置Action Servlet 映射
注意: 这一段中的材料不是针对Struts的。配置servlet映射是在Java Servlet规格书中定义的。这一段描述了最普通的针对配置一个Struts程序的方法。
又两种一般的方法来配置一个URL成为被controller servlet处理的URL - 前缀匹配和后缀匹配。每种方法相应的配置条目会在下面描述。
前缀匹配意味着你希望所有的以某一个特定值开头的URL(在上下文路径部分之后)都被送往这个servlet。这样的条目可能是这样:
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>/execute/*</url-pattern>
</servlet-mapping>
就是说前面我们提到过的匹配/logon的URI请求会被这样描述:
http://www.mycompany.com/myapplication/execute/logon
这里/myapplication是你的程序部署的上下文路径。
后缀匹配映射,相反,在URL以某一个特定的字符串结尾时把URI请求匹配到action servlet.举个例子,JSP处理servlet对应到*.jsp,这样,每当一个jsp页面被访问,它都会被调用进行处理。为了使用*.do后缀名(意思是“做什么”),映射条目可能是这样的:
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
前面我们提到过的匹配/logon的URI请求会被这样描述:
http://www.mycompany.com/myapplication/logon.do

4.5.3 配置 Struts 标签库
下一步,你必须增加一个条目来定义Struts标签库。当前Struts打包了4个标签库。
struts-bean 标签库包含访问bean和它们的属性的有用的标签,也包含利用这些访问定义新的bean以便在页面剩下的部分用脚本中的变量和页面范围的属性来访问。还有用request coolie,header和parameter的值来创建bean的便利机制。
struts-html 标签库包含用户创建struts输入表单,还有其他通常很在创建基于HTML的用户界面上很有用的标签。
struts-logic 标签库包括用于有条件的生成输出代码,循环整个对象容器来输出重复的文本,还有程序流管理。
struts-template 标签库包含定义了一个模版机制的标签。
下面是在你的程序中如何定义所有这些标签。在实际工作中你可以只指定你的程序要用到的。
<taglib>
<taglib-uri>
/WEB-INF/struts-bean.tld
</taglib-uri>
<taglib-location>
/WEB-INF/struts-bean.tld
</taglib-location>
</taglib>
<taglib>
<taglib-uri>
/WEB-INF/struts-html.tld
</taglib-uri>
<taglib-location>
/WEB-INF/struts-html.tld
</taglib-location>
</taglib>
<taglib>
<taglib-uri>
/WEB-INF/struts-logic.tld
</taglib-uri>
<taglib-location>
/WEB-INF/struts-logic.tld
</taglib-location>
</taglib>
<taglib>
<taglib-uri>
/WEB-INF/struts-template.tld
</taglib-uri>
<taglib-location>
/WEB-INF/struts-template.tld
</taglib-location>
</taglib>
这告诉JSP系统到哪里去寻找标签库的定义(在你的程序的WEB-INF目录,而不是Internet上的某处)。

4.5.4 把Struts部件增加到你的程序中
为了使用Struts,你必须拷贝你需要的.tld文件到你的WEB-INF目录,并且拷贝struts.jar(和其它所有的commons-*.jar文件)到你的WEB-INF/lib目录。
下一章: Struts 资源



--------------------------------------------------------------------------------

Copyright (c) 2000-2002, Apache Software Foundation
 
我就翻译了这么多,剩下的资源连结列表和who we are没有翻译。FAQ也没有翻译。
翻译了一天半,累死我了。
 
太感谢了 [8D]
 
看了一个半小时,累死我了,还一知半解的。
 
请发现有文法表达问题或者翻译拗口,甚至是笔误的在此写下来。我来修改。
 
>>以前,大多数web开发者使用标准的HTML能力来编写Forms
HTML能力 是个什么意思?
还有你的原文不贴出来,谁知道哪里翻译错了?
有空的话,我会仔细看看。
虽然我不理解你这篇文章的意思,但是抱着学习的目的,我会认真看看,顺便帮你找找错。
 
谢谢wukw:
原文在http://jakarta.apache.org/struts/doc-1.0.2/userGuide/index.html
翻译过的文档,HTML格式的在http://www.beaconsystem.com/pub/doc/Struts/userGuide_CN/index.html
HTML格式的内容和我在这儿贴出来的是完全一致的,但是有HTML格式会有助于阅读。
>>以前,大多数web开发者使用标准的HTML能力来编写Forms
这儿有些生硬。原文是:
At one time or another, most web developers have built forms using the standard capabilities of HTML, such as the <input> tag.
这里capabilities修饰HTML。翻译为这样可能好一些:
大多数web开发者使用标准HTML本身的功能来编写Forms
 
很好!
Thanks!
 
http://www.beaconsystem.com HTTP 404 Error,你们可以访问吗?
 
Struts 用户指南
$Id: users_guide.html,v 1.10 2000/08/10 23:45:27 craigmcc Exp $
作者:Jakarta Team http://jakarta.apache.org
译者:李锟(dlee) unruly_wind@163.net
1. 介绍
1.1 Model-View-Controller (MVC) 设计模式
FIXME - 需要一个对该模式一般性的介绍。(译注:可以参考机械工业出版社的《设计模式》。)
1.2 将MVC概念映射到Struts组件中
Struts 的体系结构实现了Model-View-Controller设计模式的概念,它将这些概念映射到web应用程序的组件和概念中,如下图所示:

--------------------------------------------------------------------------------



--------------------------------------------------------------------------------

这一体系结构中每个主要的组件都将在下面做详细的讨论。

1.3 Model: 系统状态和商业逻辑JavaBeans
基于MVC的系统中的 Model 部分可以细分为两个概念 -- 系统的内部状态, 能够改变状态的行为。用语法术语来说,我们可以把状态信息当作名词(事物),把行为当作动词(事物状态的改变)。
通常说来,你的应用程序将系统内部的状态表示为一组一个或多个的JavaBeans,使用属性(properties)来表示状态的细节。依赖于你的应用程序的复杂度,这些beans可以是自包含的(以某种方式知道怎样永久地保存它们的状态信息),或者可以是正面的(facades),知道当被请求时怎样从外部数据源(例如数据库)中取得信息。Entity EJBs通常也用来表示内部状态。

大型应用程序经常将系统可能的商业逻辑行为表示为可以被维护状态信息的beans调用的方法。举个例子,你有一个为每个当前用户保存在session中的购物车bean,里面是表示当前用户决定购买物品的属性。这个bean有一个checkOut()方法用来验证用户的信用卡,将定单发给库房以选择货品和出货。别的系统分别地表示同样的行为,或许使用Session EJBs。

在一些小型应用程序中,同样的行为又可能嵌入到作为Controller一部分的 Action 类中。这在逻辑非常简单或者并不想要在其它环境中重用这些商业逻辑时是恰当的。Struts框架支持所有这些方法,但建议将商业逻辑(“做什么”)和 Action 类(“决定做什么”)分离开。

1.4 View: JSP页面和表示组件
基于Struts的应用程序中的 View 部分通常使用JSP技术来构建。JSP页面包含称为“模版文本”的静态HTML(或XML)文本,加上插入的基于对特殊行为标记解释的动态内容。JSP环境包括了其用途由JSP规范来描述的一套标准的行为标记,例如 <jsp:useBean> 。另外,还有一个用来定义你自己标记的标准机制,这些自定义的标记组织在“定制标记库”中。
Struts包括了一个广阔的便于创建用户界面,并且充分国际化的定制标记库,与作为系统 Model 部分一部分的ActionForm beans美妙地相互配合。这些标记的使用将在后面做详细讨论。

除了JSP页面和其包含的行为及定制标记,商业对象经常需要能够基于它们在被请求时的当前状态将自己处理成HTML(或XML)。从这些对象处理过的输出可以很容易地使用 <jsp:include> 标准行为标记包括在结果的JSP页面中。

1.5 Controller: ActionServlet和ActionMapping
应用程序的 Controller 部分集中于从客户端接收请求(典型情况下是一个运行浏览器的用户),决定执行什么商业逻辑功能,然后将产生下一步用户界面的责任委派给一个适当的View组件。在Struts中,controller的基本组件是一个 ActionServlet 类的servlet。这个servlet通过定义一组映射(由Java接口 ActionMapping 描述)来配置。每个映射定义一个与所请求的URI相匹配的路径和一个 Action 类(一个实现 Action 接口的类)完整的类名,这个类负责执行预期的商业逻辑,然后将控制分派给适当的View组件来创建响应。
Struts也支持使用包含有运行框架所必需的标准属性之外的附加属性的 ActionMapping 类的能力。这允许你保存特定于你的应用程序的附加信息,同时仍可利用框架其余的特性。另外,Struts允许你定义控制将重定向到的逻辑名,这样一个行为方法可以请求“主菜单”页面(举例),而不需要知道相应的JSP页面的实际名字是什么。这个功能极大地帮助你分离控制逻辑(下一步做什么)和显示逻辑(相应的页面的名称是什么)。

2. 创建Model组件
2.1 概述
你用到的应用程序的需求文档很可能集中于创建用户界面。然而你应该保证每个提交的请求所需要的处理也要被清楚的定义。通常说来,Model 组件的开发者集中于创建支持所有功能需求的JavaBeans类。一个特殊应用要求的beans的精确特性依赖于具体需求变化会非常的大,但是它们通常可以分成下面讨论的几种类型。然而,首先对“范围”概念做一个简短的回顾是有用的,因为它与beans有关。
2.2 JavaBeans和范围
在一个基于web的应用程序中,JavaBeans可以被保存在(并从中访问)一些不同“属性”的集合中。每一个集合都有集合生存期和所保存的beans可见度的不同的规则。总的说来,定义生存期和可见度的这些规则被叫做这些beans的 范围 。JSP规范中使用以下术语定义可选的范围(在圆括号中定义servlet API中的等价物):
page - 在一个单独的JSP页面中可见的Beans,生存期限于当前请求。(service()方法中的局部变量) request - 在一个单独的JSP页面中可见的Beans,也包括所有包含于这个页面或从这个页面重定向到的页面或servlet。(Request属性)
session - 参与一个特定的用户session的所有的JSP和servlet都可见的Beans,跨越一个或多个请求。(Session属性)
application - 一个web应用程序的所有JSP页面和servlet都可见的Beans。(Servlet context属性)
记住同一个web应用程序的JSP页面和servlets共享同样一组bean集合是很重要的。例如,一个bean作为一个request属性保存在一个servlet中,就象这样:
MyCart mycart = new MyCart(...);

request.setAttribute("cart", mycart);

将立即被这个servlet重定向到的一个JSP页面使用一个标准的行为标记看到,就象这样:
<jsp:useBean id="cart" scope="request"
class="com.mycompany.MyApp.MyCart"/>
2.3 ActionForm Beans
Struts框架通常假定你已经为每一个你的应用程序中请求的输入创建了一个 ActionForm bean(即一个实现了 ActionForm 接口的类)。如果你在你的 ActionMapping 配置文件中定义了这样的beans(见“创建Controller组件”),Struts的controller servlet在调用适当的 Action 方法前将自动为你执行如下的服务:
用适当的关键字检查用户的session中是否有适当的类的bean的一个实例。
如果没有这样的session范围的bean,自动建立一个新的bean并添加到用户的session中。
对每个名字对应于bean中的一个属性的请求参数,调用相应的set方法。这个操作类似于当你以通配符“*”选择所有属性使用标准的JSP行为标记 <jsp:setProperty> 。
更新的ActionForm bean在被调用时将被传递给Acton类的perform()方法,以使这些值能够立即生效。
当你在写你的ActionForm beans时,记住以下的原则:
ActionForm 接口本身不需要特殊的实现方法。它是用来标识这些特定的beans在整个体系结构中的作用。典型情况下,一个ActionForm bean只包括属性的get方法和set方法,没有商业逻辑。
通常在一个ActionForm bean中只有很少的输入验证逻辑。这样的beans存在的主要理由是保存用户为相关的表单所输入的大部分近期值 -- 甚至在错误被检测到时 -- 这样同样的页面可以被重建,伴随有一组出错信息,这样用户仅仅需要纠正错误的字段。用户输入的验证应该在 Action 类中执行(如果是很简单的话),或者在适当的商业逻辑beans中执行。
为每个表单中出现的字段定义一个属性(用相关的getXxx()和setXxx()方法)。字段名和属性名必须按照JavaBeans的约定相匹配。例如,一个名为 username 的输入字段将引起 setUsername() 方法被调用。
你应该注意一个“表单”在这里讨论时的意义并不必须对应于用户界面中的一个单独的JSP页面。在很多应用程序中一个“表单”(从用户的观点)延伸至多个页面也是很平常的。想想看,例如,通常在安装新的应用程序时使用的导航安装程序的用户界面。Struts鼓励你定义一个包含所有字段属性的单独的ActionForm bean。不管字段实际上是显示在哪个页面上。同样的,同一表单的不同的页面应该提交到相同的Action类。如果你遵照这个建议,在大多数情况下,页面设计者可以重新组织不同页面中的字段而不需要改变处理逻辑。
2.4 系统状态Beans
系统的实际状态通常表示为一组一个或多个的JavaBeans类,其属性定义当前状态。例如,一个购物车系统包括一个表示购物车的bean,这个bean为每个单独的购物者维护,这个bean中包括(在其它事物之中)一组购物者当前选择购买的项目。分别地,系统也包括保存用户信息(包括他们的信用卡和送货地址)、可获得项目的目录和它们当前库存水平的不同的beans。
对于小规模的系统,或者对于不需要长时间保存的状态信息,一组系统状态beans可以包含所有系统曾经经历的特定细节的信息。或者经常是,系统状态beans表示永久保存在一些外部数据库中的信息(例如CustomerBean对象对应于表 CUSTOMERS 中的特定的一行),在需要时从服务器的内存中创建或清除。在大规模应用程序中,Entity EJBs也用于这种用途。

2.5 商业逻辑Beans
你应该把你的应用程序中的功能逻辑封装成对为此目的设计的JavaBeans的方法调用。这些方法可以是用于系统状态beans的相同的类的一部分,或者可以是在专门执行商业逻辑的独立的类中。在后一种情况下,你通常需要将系统状态beans传递给这些方法作为参数处理。
为了代码最大的可重用性,商业逻辑beans应该被设计和实现为它们不知道自己被执行于web应用环境中。如果你发现在你的bean中你必须import一个 javax.servlet.* 类,你就把这个商业逻辑捆绑在了web应用环境中。考虑重新组织事物使你的 Action 类(Controller任务的一部分,在下面描述)翻译所有从HTTP请求中请求被处理为对你的商业逻辑beans属性set方法调用的信息,然后可以发出一个对 execute() 的调用。这样的一个商业逻辑类可以被重用在除它们最初被构造的web应用程序以外的环境中。

依赖于你的应用程序的复杂度和范围,商业逻辑beans可以是与作为参数传递的系统状态beans交互作用的普通的JavaBeans,或者使用JDBC调用访问数据库的普通的JavaBeans。而对于较大的应用程序,这些beans经常是有状态或无状态的EJBs。

2.6 题外话: 访问关系数据库
很多web应用程序利用一个关系数据库(通过一个JDBC driver访问)来保存应用程序相关的永久数据。其它应用程序则使用Entity EJBs来实现这个目的,他们委派EJBs自己来决定怎样维护永久状态。如果你是使用EJBs来实现这个目的,遵照EJB规范中描述的客户端设计模式。
对于基于直接数据库访问的web应用程序,一个普通的设计问题是当需要访问低层数据库时怎样产生一个适当的JDBC连接对象。解决这个问题有几种方法 -- 以下原则描述了推荐的一种方法:

创建或得到一个允许一组数据库连接被多个用户共享的ConnectionPool类。Struts(当前)没有包括这样的一个类,但是有很多这样的类可以得到。
当应用程序初始化时,在应用程序展开(deployment)描述符中定义一个有一个“启动时加载”值的servlet。我们将把这个servlet叫做 启动 servlet。在大多数情况下,这个servlet不需要处理任何的请求,所以没有一个 <servlet-mapping> 会指向它。
在启动servlet的 init() 方法中,配置并初始化一个ConnectionPool类的实例,将其保存为一个servlet context属性(从JSP的观点看等同于一个application范围的bean)。通常基于传递给启动servlet初始化参数来配置联接缓冲池是很方便的。
在启动servlet的 destroy() 方法中,包含了释放联接缓冲池所打开的联接的逻辑。这个方法将在servlet容器结束这个应用程序的时候被调用。
当 Action 类需要调用一个需要数据库联接的商业逻辑bean中的方法(例如“insert a new customer”)时,将执行下面的步骤:
为这个web应用程序从servelt context属性中得到一个联接缓冲池对象。
调用联接缓冲池对象的 open() 方法来得到一个在 Action 类调用中使用的联接。
调用商业逻辑bean中合适的方法,将数据库联接对象作为一个参数传递给它。
调用分配的联接中的 close() 方法,这将引起这个联接为了以后其它请求的重用被返回到缓冲池中。
一个通常的编程错误是忘记了把数据库联接返回给缓冲池,这将最终导致用完所有的联接。一定要确信 Action 类的逻辑总是返回联接,甚至在商业逻辑bean抛出一个违例时。
遵照上面推荐的设计模式意味着你能够编写你的商业逻辑类而不需要担心它们怎样得到一个JDBC联接来使用-- 简单地在任何需要访问数据库的方法中包含一个Connection参数。当你的商业逻辑类在一个web应用程序中被利用时,分配和释放适当的联接是 Action 类的责任。当你使用相同的商业逻辑类时,例如,在一个批处理工作中,提供一个适当的联接是那个应用程序的责任(这不需要从缓冲池中获得,因为大多数批处理工作运行于一个单线程环境中)。
3. 创建View组件
3.1 概述
这一章集中于创建应用程序中的 View 组件的任务,主要使用JSP技术建立。特别的,Struts除了提供了与输入表单的交互外还提供了建立国际化应用程序的支持。几个其它的与View相关的主题也被简单地讨论。
3.2 国际化消息
几年之前,应用程序开发者能够考虑到仅仅支持他们本国的只使用一种语言(或者有时候是两种)和通常只有一种数量表现方式(例如日期、数字、货币值)的居民。然而,基于web技术的应用程序的爆炸性增长,以及将这些应用程序展开在Internet或其它被广泛访问的网络之上,已经在很多情况下使得国家的边界淡化到不可见。这种情况转变成为一种对于应用程序支持国际化(经常被称做“i18n”,因为18是字母“i”和字母“n”之间的字母个数)和本地化的需求。
Struts建立于Java平台之上为建立国际化和本地化的应用程序提供帮助。需要熟悉的关键概念是:

Locale - 基础的支持国际化的Java类是 java.util.Locale 。每个 Locale 代表一个特别的国家和语言选择(加上一个可选的语言变量),以及一套格式假定,例如数字和日期等等。
ResourceBundle - java.util.ResourceBundle 类提供支持多种语言消息的基本工具。查看文档中关于ResourceBundle 类以及你的JDK版本的文档包中关于国际化的更多内容。
PropertyResourceBundle - 一个 ResourceBundle 类的标准实现允许你使用与初始化properties文件同样的“name=value”语法来定义资源。这对于使用为用于一个web应用程序的消息准备资源包是非常方便的,因为这些消息通常都是面向文本的。
MessageFormat - java.text.MessageFormat 类允许你使用运行时指定的参数替换一个消息字符串中的一部分(在这种情况下,是一个从一个资源包得到的消息)。这在你创建一个句子的场合中是有用的,但是词会以不同的语言按照不同的顺序出现。消息中的占位符字符串{0}用第一个运行时参数替换,{1}用第二个运行时参数替换,以此类推。
MessageResources - Struts的类 org.apache.struts.util.MessageResources 使你能够将一套资源包视做一个数据库,并且允许你为一个特定的Locale(通常是与当前用户相对应)请求一个特定的消息,而不是为服务器运行在其中的缺省的Locale请求消息。
对于一个国际化的应用程序,遵照JDK文档包中国际化文档所描述的步骤来创建一个包含每种语言的消息的属性文件。下面举一个例子说明。
假设你的源代码建立在包 com.mycompany.mypackage 中,因此它保存于一个叫做(相对于你的源目录)com/mycompany/mypackage 的目录中。为创建一个叫做 com.mycompany.mypackage.MyResources 的资源包,你应该在目录 com/mycompany/mypackage 中创建下列文件:

MyResources.properties - 包含你的服务器的缺省语言的消息。如果你的缺省语言是英语,你可能有一个这样的条目:
prompt.hello=Hello
MyResources_xx.properties - 包含ISO语言编程为“xx”(查看ResourceBundle的Java文档页面得到一个当前列表的联接)的同样的消息。对于上面的消息的法语版,你可以有这个条目:
prompt.hello=Bonjour
你可以有你需要的任意多的语言的资源包文件。
当你在web应用程序展开描述符中配置controller servlet时,你需要在一个初始化参数中定义的一件事是应用程序的资源包的基础名。在上述的情况中,这应该是 com.mycompany.mypackage.MyResources 。
3.3 表单和FormBean的交互
大部分web开发者曾经使用HTML的标准性能来建立表单,例如使用 <input> 标记。用户希望交互程序具有一定的行为,这些期待中的一个与错误处理有关 -- 如果用户出现一个错误,应用程序应该允许他们仅仅修改需要修改的部分 -- 而不需要重新敲入当前页面或表单中的任何其它信息。
使用标准的HTML和JSP编程来完全实现这个期望是单调而繁重的。举例来说,一个用户名字段的输入元素看起来可以象是这样(在JSP中)

<input type="text" name="username"
value="<%= loginBean.getUsername() %>">
这很难敲对,会把没有编程概念的HTML开发者搞糊涂,并且会在HTML编辑器中造成问题。取而代之的是,Struts提供了一种全面的基于JSP 1.1的定制标记库功能的机制来建立表单。上面的情况使用Struts处理后将象是这样:
<struts:text name="username"/>
没有必要再显式地涉及到从中获得初始值的JavaBean。这将由框架自动处理。
3.3.1 使用Struts建立表单
一个完整的注册表单将演示Struts相对于直接使用HTML和标准的JSP功能怎样极大地减轻了处理表单的痛苦。考虑以下称为logon.jsp的页面(来自Struts的例子程序):
<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts.tld" prefix="struts" %>

<html>
<head>
<title><struts:message key="logon.title"/></title>
<body bgcolor="white">

<struts:errors/>

<struts:form action="logon.do" name="logonForm"
type="org.apache.struts.example.LogonForm"/>
<table border="0" width="100%">
<tr>
<th align="right">
<struts:message key="prompt.username"/>
</th>
<td align="left">
<struts:text name="username" size="16"/>
</td>
</tr>
<tr>
<th align="right">
<struts:message key="prompt.password"/>
</th>
<td align="left">
<struts:password name="password" size="16"/>
</td>
</tr>
<tr>
<td align="right">
<struts:submit>
<struts:message key="button.submit"/>
</struts:submit>
</td>
<td align="right">
<struts:reset>
<struts:message key="button.reset"/>
</struts:reset>
</td>
</tr>
</table>
</struts:form>

</body>
</html>
下面的条目基于这个例子演示在Struts中处理表单的关键的特性:
taglib指令告诉JSP页面编译器从哪里找到Struts标记库的 标记库描述符 。在这种情况下,我们使用struts作为前缀来标识来自这个库中的标记,但是可以使用任何你想用的前缀。
这个页面使用了几个 message 标记来从一个包含有这个应用程序所有资源的 MessageResources 对象中查找国际化的消息字符串。为了让这个页面能够工作,以下的消息关键字必须在这些资源中被定义:
logon.title - 注册页面的标题
prompt.username - 一个 “Username:” 提示字符串
prompt.password - 一个 “Password:” 提示字符串
button.submit - “Submit”按钮的标签
button.reset - “Reset”按钮的标签
当用户注册时,应用程序可以在用户的session中保存一个 Locale 对象。这个 Locale 将用来选择适当语言的消息。这使得给用户一个切换语言的可选项实现起来变的容易了 -- 仅仅改变保存的 Locale 对象,所有的消息就会自动切换。
errors 标记显示由一个商业逻辑组件保存的任何出错消息,或者如果没有出错消息保存就什么都没有。这个标记将在下面做深入的描述。
form 标记基于指定的属性对一个HTML <form> 元素进行处理。它也将所有在这个表单中的字段与一个保存在关键字 logonForm 下的session范围的FormBean相关联。这个bean用来为所有的具有与bean中的属性名匹配的名字的输入字段提供初始值。如果适当的bean没有找到,一个新的bean将会被自动建立,使用指定的Java类名。
text 标记对一个类型为“text”的HTML <input> 元素进行处理。在这种情况下,占据浏览器屏幕的字符位置的数字也被指定。当页面被执行时,是相对应的bean的 username 属性的当前值(也就是 getUsername() 的返回值)。
password 标记使用方法类似。不同之处是当用户敲入他们的口令时浏览器将回应星号字符,而不是输入值。
submit 和 reset 标记在表单低部生成相应的按钮。每个按钮的文本标签使用 message 标记建立,同时带有提示,这样这些值就是国际化的。
3.3.2 输入字段类型支持
Struts为所有以下类型的输入字段定义了标记,带有与其相应的参考信息的超联接。
checkboxes
hidden 字段
password 输入字段
radio 按钮
reset 按钮
select 列表和嵌入的
options
submit 按钮
text 输入字段
textareas
在所有情况下,一个字段标记都必须嵌套在一个 form 标记中,这样字段才知道使用哪个bean来初始化显示的值。
3.3.3 其它有用的表示标记
在Struts的标记库中有几个其它的标记对于建立用户界面是有帮助的:
enumerate 为一个指定集合的每个元素重复一次标记体(可以是一个Enumeration,一个Hashtable,一个Vector或一个对象数组)。
getProperty 从指定的bean中得到指定的属性,并且在本页面的其余部分作为一个page范围的bean存在。这是访问一个被 enumerate 使用的集合的方便的方法。
ifAttributeExists 只有在一个指定的属性存在于一个指定的范围中时才对标记体求值。
ifAttributeMissing 只有在一个指定的属性不存在于一个指定的范围中时才对标记体求值。
ifParameterEquals 只有在一个指定的请求参数具有一个指定的值时才对标记体求值。
ifParameterNotEquals 只有在一个指定的请求参数不具有一个指定的值或者不存在时才对标记体求值。
ifParameterNotNull 只有在一个指定的请求参数包含在这个请求中并且长度大于0时才对标记体求值。
ifParameterNull 只有在一个指定的请求参数不包含在这个请求中或者长度等于0时才对标记体求值。
iterate 为一个指定集合中的每个元素重复一次标记体(可以是一个Collection,一个Iterator,一个Map,或者一个对象数组)。这个标记在Java2环境中代替了 enumerate 标记。
link 生成一个超联接,当没有cookie支持时自动应用URL编程来维护session状态。
parameter 处理指定请求参数的值,适当地过滤HTML中有特殊含义的字符。
property 显示一个表单中命名的bean属性 -- 在属性应该是只读时使用这个标记而不是 text 标记。
3.3.4 自动表单验证
除了上面描述的表单和bean的交互外,如果你的bean知道怎样验证它接收的输入字段,Struts还提供一种附加的机制。为了利用这个特性,使你的bean类实现 ValidatingActionForm 接口,而不是 ActionForm 接口。一个 ValidatingActionForm 增加了一个附加的方法签名:
public String[] validate()
对于一个被controller servlet在bean属性已经组装但是在相应的行为类的 perform() 方法被调用之前调用的方法,validate() 方法有以下可选项:
执行适当的验证发现没有错误 -- 返回 null 或者一个非0长度字符串数组,并且controller servlet将继续调用适当的 Action 类的 perform() 方法。
执行适当的验证发现有错误 -- 返回一个内容为应该被显示的出错消息关键字(进入应用程序的MessageResources 包)的字符串数组。controller servlet将作为适合于 <struts:errors> 标记使用的请求属性保存这个数组,并且将控制重定向回输入表单(由这个 ActionMapping 的 inputForm 属性标识)。
正如以前提到的,这个特性完全是可选的。如果你的form bean 仅仅实现了 ActionForm 接口,controller servlet将假设任何请求的验证由action类完成。
3.4 其它的表示技术
尽管你的应用程序的外表和感觉可以完全基于标准的JSP能力和Struts的定制标记库构建,你也应该考虑展开其它改进组件重用、减少管理负担或者减少出错的技术。在下面的部分讨论几个可选的技术。
3.4.1 特定于应用程序的定制标记
在使用Struts库提供的定制标记之外,很容易建立特定于你创建的应用程序的标记来帮助建立用户界面。Struts包括的例子程序用建立以下仅用于实现这个应用程序的标记演示了这个原则:
checkLogon - 检查一个特殊的会话对象的存在,如果不存在将控制重定向到注册页面。这是用来捕捉这样的情况,用户在你的应用程序执行的中间把一个页面做成书签并且试图跳过注册,或者用户的会话超时。
linkSubscription - 为一个详细的定单页面生成一个超联接,它将需要的主关键字值作为一个请求属性传递。这在列出与一个用户相关的定单并提供编辑或删除定单的联接时使用。
linkUser - 为一个用户的一个具体的页面生成一个超联接,它将它将需要的主关键字值作为一个请求属性传递。
这些标记的源代码在 src/example 目录中,在包 org.apache.struts.example 里,还带有一些其它的用在这个应用程序中的Java类。
3.4.2 有包含文件的页面组件
在一个JSP文件(包含定制标记和beans用来访问请求的动态数据)中创建完整的表示是一种非常普通的设计方法,在Struts包括的例子程序中被采用。然而很多应用程序要求在单独一个页面中显示你的应用程序的多个逻辑上独立的部分。
举例来说,一个入口应用程序可以在入口的主页面上有一些或者全部以下的功能:

访问这个入口的一个搜索引擎。
一个或更多的“提供新闻”的显示,含有按照用户的注册信息定制的感兴趣的标题。
访问与这个入口相关的讨论的主题。
如果你的入口提供免费邮件帐号,还要有一个“邮件等待”的提示。
如果你能够将工作划分开,分配不同的开发者去做不同的片段,那么这个站点不同片段的开发就会更加简单。然后,你可以使用JSP技术的 include 能力来将这些片段组合进一个单独的页面。有两种 include 可用,依赖于你希望输出的组合发生在什么时间:
include 指令 (<%@ include file="xxxxx" %>)在JSP页面被编译时处理。它用于包括不需要在请求时改变的HTML代码。它把包括进来的文本当作静态文本,很象C或C++中的 #include 指令。
include 行为 (<jsp:include page="xxxxx" flush="true" />)在请求时处理,并且是由服务器透明处理。这意味着你可以通过把它嵌套在一个类似ifParameterEquals的标记中有条件地执行include 。
3.4.3 图片处理组件
一些应用程序要求动态生成图片,就象一个股市报告站点的价格图一样。通常使用两种不同的方法来实现这个需求:
处理一个执行一个servlet请求的URL的超联接。这个servlet将使用一个图象库来生成图片,设置适当的content类型(例如 image/gif),并且将图片的字节流发送回浏览器。浏览器就会象从一个静态文件中接收到的一样显示图片。
处理HTML代码需要下载一个创建请求的图象的Java applet。你可以通过为在处理的代码中的这个applet设置适当的初始化参数配置这个图象,或者你可以让这个applet与服务器建立自己联接来接收这些参数。
4. 创建Controller组件
4.1 概述
现在我们理解了怎样构造你的应用程序的Model和View组件,现在是集中到 Controller 组件的时候了。Struts包括一个实现映射一个请求URI到一个行为类的主要功能的servlet。因此你的与Controller有关的主要责任是:
为每一个可能接收的逻辑请求写一个 Action 类(也就是,一个 Action 接口的实现)
写一个定义类名和与每个可能的映射相关的其它信息的 ActionMapping 类(也就是,一个 ActionMapping 接口的实现)
写行为映射配置文件(用XML)用来配置controller servlet。
为你的应用程序更新web应用程序展开描述符文件(用XML)用来包括必需的Struts组件。
给你的应用程序添加适当的Struts组件。
4.2 Action类
Action 接口定义一个单一的必须由一个 Action 类实现的方法,就象下面这样:
public ActionForward perform(ActionServlet servlet,
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException;

一个 Action 类的目标是处理这个请求,然后返回一个标识JSP页面的 ActionForward 对象,控制应该重定向这个JSP页面以生成相应的响应。在 Model 2 设计模式中,一个典型的 Action 类将在它的 perform() 方法中实现下面的逻辑:
验证用户session的当前状态(例如,检查用户已经成功地注册)。如果 Action 类发现没有注册存在,请求应该重定向到显示用户名和口令用于注册的JSP页面。应该这样做是因为用户可能试图从“中间”(也就是,从一个书签)进入你的应用程序,或者因为session已经超时并且servlet容器创建了一个新的session。
如果验证还没有发生(由于使用一个实现 ValidatingActionForm 接口的form bean),验证这个 form bean 的属性是必须的。如果发现一个问题,当作一个请求属性保存合适的出错信息关键字,然后将控制重定向回输入表单这样错误可以被纠正。
执行要求的处理来处理这个请求(例如在数据库里保存一行)。这可以用嵌入 Action 类本身的代码来完成,但是通常应该调用一个商业逻辑bean的一个合适的方法来执行。
更新将用来创建下一个用户界面页面的服务器端对象(典型情况下是request范围或session范围beans,定义你需要在多长时间内保持这些项目可获得)。
返回一个标识生成响应的JSP页面的适当的 ActionForward 对象,基于新近更新的beans。典型情况下,你将通过在你接收到的 ActionMapping 对象(如果你使用一个局部于与这个映射上的逻辑名)或者在controller servlet 本身(如果你使用一个全局于应用程序的逻辑名)上调用 findForward() 得到一个对这样一个对象的引用。
当为 Action 类编程时要记住的设计要点包括以下这些:
controller servlet仅仅创建一个你的 Action 类的实例,用于所有的请求。这样你需要编写你的 Action 类使其能够在一个多线程环境中正确运行,就象你必须安全地编写一个servlet的 service() 方法一样。
帮助线程安全编程的最重要的原则就是在你的 Action 类中仅仅使用局部变量而不是实例变量。局部变量创建于一个分配给(由你的JVM)每个请求线程的栈中,所以没有必要担心会共享它们。
尽管不应该,代表你的系统中Model部分的的beans仍有可能抛出违例。你应该在你的 perform() 方法的逻辑中捕捉所有这样的违例,并且通过执行以下语句将它们记录在应用程序的日志文件中(包括相应的栈跟踪信息):
servlet.log("Error message text", exception);

作为一个通用的规则,分配很少的资源并在来自同一个用户(在用户的session中)的请求间保持它们会导致可伸缩性的问题。你应该在将控制重定向到适当的View组件前努力释放这样的资源(例如数据库联接) -- 甚至在你调用的一个bean抛出了一个违例时。
另外,你将会想要防止出现非常大的 Action 类。最简单的实现途径是将你的功能逻辑嵌入到 Action 类本身,而不是将其写在独立的商业逻辑beans中。除了使 Action 类难于理解和维护外,这种方法也使得难于重用这些商业逻辑代码,因为代码被嵌入到一个组件(Action 类)中并被捆绑运行于web应用程序环境中。
包括在Struts中的例子程序某种程度上延伸了这个设计原则,因为商业逻辑本身是嵌入到 Action 类中的。这应该被看作是在这个样本应用程序设计中的一个bug,而不是一个Struts体系结构中的固有特性,或者是一个值得仿效的方法。

4.3 ActionMapping实现
为了成功地运行,Struts的controller servlet需要知道关于每个URI该怎样映射到一个适当的 Action 类的几件事。需要了解的知识封装在一个叫做 ActionMapping 的Java接口中,它有以下属性:
actionClass - 用于这个映射的 Action 类完整的Java类名。第一次一个特定的映射被使用,一个这个类的实例将被创建并为以后重用而保存。
formAttribute - session范围的bean的名字,当前的这个映射的 ActionForm 被保存在这个bean之下。如果这个属性没有被定义,没有 ActionForm 被使用。
formClass - 用于这个映射的 ActionForm 类完整的Java类名。如果你在使用对form beans的支持,这个类的一个实例将被创建并保存(在当前的用户会话中)
path - 匹配选择这个映射的请求的URI路径。看下面如何匹配的例子。
Struts在一个叫做 ActionMappingBase 的类中包括了一个 ActionMapping 接口的方便的实现。如果你不需要为你自己的映射定义任何附加的属性,尽管把这个类作为你的 ActionMapping 类好了,就向下面部分描述的那样配置。然而,定义一个 ActionMapping 实现(多半是扩展 ActionMappingBase 类)来包含附加的属性也是可能的。controller servlet知道怎样自动配置这些定制属性,因为它使用Struts的Digester模块来读配置文件。
包括在Struts的例子程序中,这个特性用来定义两个附加的属性:

failure - 如果Action类检测到它接收的输入字段的一些问题,控制应该被重定向到的上下文相关的URI。典型情况下是请求发向的JSP页面名,它将引起表单被重新显示(包含Action类设置的出错消息和大部分最近的来自ActionForm bean的输入值)。
success - 如果Action类成功执行请求的功能,控制应该被重定向到的上下文相关的URI。典型情况下是准备这个应用程序的会话流的下一个页面的JSP页面名。
使用这两个额外的属性,例子程序中的 Action 类几乎完全独立于页面设计者使用的实际的JSP页面名。这个页面可以在重新设计时被重命名,然而几乎不会影响到 Action 类本身。如果“下一个”JSP页面的名字被硬编码到 Action 类中,所有的这些类也需要被修改。
4.4 Action映射配置文件
controller servlet怎样知道你想要得到的映射?写一个简单地初始化新的 ActionMapping 实例并且调用所有适当的set方法的小的Java类是可能的(但是很麻烦)。为了使这个处理简单些,Struts包括一个Digester模块能够处理一个想得到的映射的基于XML的描述,同时创建适当的对象。看 API 文档 以获得关于Digester更多的信息。
开发者的责任是创建一个叫做 action.xml 的XML文件,并且把它放在你的应用程序的WEB-INF目录中。(注意这个文件并不需要 DTD,因为实际使用的属性对于不同的用户可以是不同的)最外面的XML元素必须是<action-mappings>,在这个元素之中是嵌入的0个或更多的 <action> 元素 -- 每一个对应于你希望定义的一个映射。

来自例子程序的 action.xml 文件包括“注册”功能的以下映射条目,我们用来说明这个需求:

<action-mappings>

<forward name="logon" path="/logon.jsp"/>

<action path="/logon"
actionClass="org.apache.struts.example.LogonAction"
formAttribute="logonForm"
formClass="org.apache.struts.example.LogonForm"
inputForm="/logon.jsp">
<forward name="success" path="/mainMenu.jsp"/>
</action>

</action-mappings>
就象你所看到的,这个映射匹配路径 /logon (实际上,因为例子程序使用扩展匹配,你在一个JSP页面指定的请求的URI结束于/logon.do)。当接收到一个匹配这个路径的请求时,一个 LogonAction 类的实例将被创建(仅仅在第一次)并被使用。controller servlet将在关键字 logonForm 下查找一个session范围的bean,如果需要就为指定的类创建并保存一个bean。
这个 action 元素也定义了一个逻辑名“success”,它在 LogonAction 类中被用来标识当一个用户成功注册时使用的页面。象这样使用一个逻辑名允许将 action 类隔离于任何由于重新设计位置而可能发生的页面名改变。

这是第二个在任何 action 之外宣告的 forward 元素,这样它就可以被所有的action全局地获得。在这个情况下,它为注册页面定义了一个逻辑名。当你调用 mapping.findForward() 时在你的 action 代码中,Struts首先查找这个action本地定义的逻辑名。如果没有找到,Struts会自动为你查找全局定义的逻辑名。

4.5 Web应用程序展开描述符
设置应用程序最后的步骤是配置应用程序展开描述符(保存在文件WEB-INF/web.xml中)以包括所有必需的Struts组件。作为一个指南使用例子程序的展开描述符,我们看到下面的条目需要被创建或修改。
4.5.1 配置Action Servlet实例
添加一个条目定义action servlet本身,同时包括适当的初始化参数。这样一个条目看起来象是这样:
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-p
aram>
<param-name>application</param-name>
<param-value>org.apache.struts.example.ApplicationResources</param-value>
</init-param>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/action.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>mapping</param-name>
<param-value>org.apache.struts.example.ApplicationMapping</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
controller servlet支持的初始化参数在下面描述,拷贝自 ActionServlet 类的 Javadocs 。方括号描述如果你没有为那个初始化参数提供一个值时假设的缺省值。
application - 应用程序资源包基类的Java类名。[NONE].
config - 包含配置信息的XML资源的上下文相关的路径。[/WEB-INF/action.xml]
debug - 这个servlet的调试级别,它控制记录多少信息到日志中。[0]
digester - 我们在 initMapping() 中利用的Digester的调试级别,它记录到System.out而不是servlet的日志中。[0]
forward - 使用的ActionForward实现的Java类名。[org.apache.struts.action.ActionForward]
mapping - 使用的ActionMapping实现的Java类名。[org.apache.struts.action.ActionMappingBase]
nocache - 如果设置为 true,增加HTTP头信息到所有响应中使浏览器对于生成或重定向到的任何响应不做缓冲。[false]
null - 如果设置为 true,设置应用程序资源使得如果未知的消息关键字被使用则返回 null。否则,一个包括不欢迎的消息关键字的出错消息将被返回。[true]
4.5.2 配置Action Servlet映射
有两种通常的方法来定义将被controller servlet处理的URL -- 前缀匹配和扩展匹配。每种方法的一个适当的映射条目将在下面被描述。
前缀匹配意思是你想让所有以一个特殊值开头(在上下文路径部分之后)的URL传递给这个servlet。这样一个条目看起来可以象是这样:

<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>/execute/*</url-pattern>
</servlet-mapping>
它意味着一个匹配前面描述的 /logon 路径的请求的URL看起来象是这样:
http://www.mycompany.com/myapplication/execute/logon
这里 /myapplicationis 是你的应用程序展开所在的上下文路径。
另一方面,扩展映射基于URL以一个跟着定义的一组字符的句点结束的事实而将URL匹配到action servlet 。例如,JSP处理servlet映射到 *.jsp 模式这样它在每个JSP页面请求时被调用。为了使用 *.do 扩展(它意味着“做某件事”)映射条目看起来应该象是这样:

<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
并且一个匹配以前描述的 /logon 路径的请求的URI可以看起来象是这样:
http://www.mycompany.com/myapplication/logon.do
4.5.3 配置Struts标记库
下一步,你必须添加一个定义Struts标记库的条目。这个条目看起来应该象是这样:
<taglib>
<taglib-uri>/WEB-INF/struts.tld</taglib-uri>
<taglib-location>/WEB-INF/struts.tld</taglib-location>
</taglib>
它告诉JSP系统到哪里去找这个库的标记库描述符(在你的应用程序的WEB-INF目录,而不是在外部互联网上的某个地方)。
4.5.4 添加Struts组件到你的应用程序中
为了在你的应用程序运行时使用Struts,你必须将 struts.tld 文件拷贝到你的 WEB-INF 目录,将 struts.jar 文件拷贝到你的 WEB-INF/lib 。 ?

 
wow.你在哪儿找到的?URL给我。
 
我找到了。http://www.linuxforum.net/doc/strutsuserguide.html
版本不同。
他的是一个比较老的版本。
我的版本是1.0.3正式版,它的其实是1.0 beta1
我们的翻译原则也不同,比如我对于编程可能用到的名词,都保留原英文,在后面用括号
注释中文,他就直接翻译成了中文。比如对于'Scope'一词,在2.2节。
哈,翻译的人越多越好噢。
 
在找jrun studio注册版的时候,无意中连出一个网址,真的很不错!!!!
虽然我所学甚浅,但是我能感觉到这里有很多好文章。收藏了好几篇。
http://www.code-labs.com/
 
曹晓钢:
能告诉我Struts是做什么的么?谢谢!
 

Similar threads

D
回复
0
查看
1K
DelphiTeacher的专栏
D
S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
D
回复
0
查看
1K
DelphiTeacher的专栏
D
D
回复
0
查看
708
DelphiTeacher的专栏
D
后退
顶部