作者: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中的等价物):
MyCart mycart = new MyCart(...); request.setAttribute("cart", mycart);将立即被这个servlet重定向到的一个JSP页面使用一个标准的行为标记看到,就象这样: <jsp:useBean id="cart" scope="request" class="com.mycompany.MyApp.MyCart"/> 2.3 ActionForm BeansStruts框架通常假定你已经为每一个你的应用程序中请求的输入创建了一个 ActionForm bean(即一个实现了 ActionForm 接口的类)。如果你在你的 ActionMapping 配置文件中定义了这样的beans(见“创建Controller组件”),Struts的controller servlet在调用适当的 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连接对象。解决这个问题有几种方法 -- 以下原则描述了推荐的一种方法:
3. 创建View组件3.1 概述这一章集中于创建应用程序中的 View 组件的任务,主要使用JSP技术建立。特别的,Struts除了提供了与输入表单的交互外还提供了建立国际化应用程序的支持。几个其它的与View相关的主题也被简单地讨论。3.2 国际化消息几年之前,应用程序开发者能够考虑到仅仅支持他们本国的只使用一种语言(或者有时候是两种)和通常只有一种数量表现方式(例如日期、数字、货币值)的居民。然而,基于web技术的应用程序的爆炸性增长,以及将这些应用程序展开在Internet或其它被广泛访问的网络之上,已经在很多情况下使得国家的边界淡化到不可见。这种情况转变成为一种对于应用程序支持国际化(经常被称做“i18n”,因为18是字母“i”和字母“n”之间的字母个数)和本地化的需求。Struts建立于Java平台之上为建立国际化和本地化的应用程序提供帮助。需要熟悉的关键概念是:
假设你的源代码建立在包 com.mycompany.mypackage 中,因此它保存于一个叫做(相对于你的源目录)com/mycompany/mypackage 的目录中。为创建一个叫做 com.mycompany.mypackage.MyResources 的资源包,你应该在目录 com/mycompany/mypackage 中创建下列文件:
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中处理表单的关键的特性:
3.3.2 输入字段类型支持Struts为所有以下类型的输入字段定义了标记,带有与其相应的参考信息的超联接。
3.3.3 其它有用的表示标记在Struts的标记库中有几个其它的标记对于建立用户界面是有帮助的:
3.3.4 自动表单验证除了上面描述的表单和bean的交互外,如果你的bean知道怎样验证它接收的输入字段,Struts还提供一种附加的机制。为了利用这个特性,使你的bean类实现 ValidatingActionForm 接口,而不是 ActionForm 接口。一个 ValidatingActionForm 增加了一个附加的方法签名:public String[] validate()对于一个被controller servlet在bean属性已经组装但是在相应的行为类的 perform() 方法被调用之前调用的方法,validate() 方法有以下可选项:
3.4 其它的表示技术尽管你的应用程序的外表和感觉可以完全基于标准的JSP能力和Struts的定制标记库构建,你也应该考虑展开其它改进组件重用、减少管理负担或者减少出错的技术。在下面的部分讨论几个可选的技术。3.4.1 特定于应用程序的定制标记在使用Struts库提供的定制标记之外,很容易建立特定于你创建的应用程序的标记来帮助建立用户界面。Struts包括的例子程序用建立以下仅用于实现这个应用程序的标记演示了这个原则:
3.4.2 有包含文件的页面组件在一个JSP文件(包含定制标记和beans用来访问请求的动态数据)中创建完整的表示是一种非常普通的设计方法,在Struts包括的例子程序中被采用。然而很多应用程序要求在单独一个页面中显示你的应用程序的多个逻辑上独立的部分。举例来说,一个入口应用程序可以在入口的主页面上有一些或者全部以下的功能:
3.4.3 图片处理组件一些应用程序要求动态生成图片,就象一个股市报告站点的价格图一样。通常使用两种不同的方法来实现这个需求:
4. 创建Controller组件4.1 概述现在我们理解了怎样构造你的应用程序的Model和View组件,现在是集中到 Controller 组件的时候了。Struts包括一个实现映射一个请求URI到一个行为类的主要功能的servlet。因此你的与Controller有关的主要责任是:
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() 方法中实现下面的逻辑:
包括在Struts中的例子程序某种程度上延伸了这个设计原则,因为商业逻辑本身是嵌入到 Action 类中的。这应该被看作是在这个样本应用程序设计中的一个bug,而不是一个Struts体系结构中的固有特性,或者是一个值得仿效的方法。 4.3 ActionMapping实现为了成功地运行,Struts的controller servlet需要知道关于每个URI该怎样映射到一个适当的 Action 类的几件事。需要了解的知识封装在一个叫做 ActionMapping 的Java接口中,它有以下属性:
包括在Struts的例子程序中,这个特性用来定义两个附加的属性:
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 。方括号描述如果你没有为那个初始化参数提供一个值时假设的缺省值。
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 。 ?
|
Struts 用户指南
最新推荐文章于 2024-09-21 15:09:40 发布