Struts快速入门

 

Struts快速入门:它能做什么?它如何做?

本文选自《Practical J2EE Application Architecture》的第四章,作者是Nadir Gulzar,并且由McGraw-Hill/Osborne Media(www.osborne.com)于2003年3月出版

Published on TheServerSide.com

November 4, 2002

发布于 TheServerSide.com

2002年11月4日

简介

本文的目标是为预期的Struts用户介绍使用Struts的最大好处,并同时举例说明它的配置和使用方法。我们将定义一个完全表述的架构的需求并且同时讨论如何在Struts架构中实现这些需求。我们同样也探讨了Struts中实现的设计模式即控制器的和相关的辅助组件的语义。这些知识将会在设计与架构相互影响的组件的时候发挥作用,并且这也是为扩展该框架以适合某个项目特殊要求的必需。本文补足了http://jakarta.apache.org/struts提供的资料。

MVC 结构

MVC结构是一种分解一个应用系统至三个部分(模型,视图,控制器)的方法。原本应用于图形用户界面(GUI)的输、处理和输出模型

图略,见原书图。

模型

模型表示一个应用系统的数据并且包含访问和管理该数据的逻辑。所有属于应用系统持久状态的数据都应该保存于模型的对象里。模型提供的服务必须足够普适于不同的终端。通过粗览模型的公共方法列表,应该易于了解如何控制模型的行为。一个模型聚集了相关的数据和操作以提供一个详细而精确的服务;这些包装并抽象在操作中的事物功能被模型化。一个模型的接口提供了访问和更新模型状态的、执行封装在模型中的复杂进程的方法。模型服务被控制器访问,用于查询或使模型的状态发生变化。当状态发生变化时,模型会通报给视图。

视图

视图的责任是表现模型的状态。表述语句封装于视图中,因此模型数据可以适合多种不同的终端。当模型中的变化传达到视图的时候,视图会修改自己。视图将用户输入传递到控制器。

控制器

控制器的任务是获取并翻译用户输入到动作并由模型执行。控制器的任务还有根据用户输入和执行结果选择下一个视图。在基于J2EE的应用系统中,MVC结构被用于分离由JavaBeans或EJB表示的事物层功能和由JSP表示的表现层,中间经由基于Servlet的控制器。然而,控制器设计必须适合各种不同类型终端的输入,包括来自Web终端的HTTP请求,无线终端的WML,供应商与业务伙伴的基于XML的文档等。对于HTTP请求和任务范例,提交的HTTP请求被发送到一个控制中心,依次解释并委派该请求到适当的请求处理器。这也被作为第二类MVC结构。请求处理器与开发者提供的架构结合起来,以实现对与模型有关联的特定逻辑的请求。依靠这个交互的结果,控制器可以决定下一个视图以产生恰当的反应。

Struts MVC 语义

我们从关键的Struts抽象概念开始,这是它MVC架构的核心。Struts使用Service to Worker模式实现了MVC模式。[Core]

Struts MVC 语义

我们从关键的Struts抽象概念开始,这是它MVC架构的核心。Struts使用Service to Worker模式实现了MVC模式。[Core]

控制器对象

控制器被ActionServlet类所实现。它提供一个中心位置来处理全部的终端请求。这就为处理视图和导航管理的控制层提供了更为清晰的划分,把模型访问和操作留给专门的请求处理器。全部提交的请求被映射到中心处理器,其配置说明如下:

<servlet>

<servlet-name>action</servlet-name>

<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>

</servlet>

全部的请求URI以*.do的模式存在并映射到这个servlet,其配置如下:

<servlet-mapping>

<servlet-name>action</servlet-name>

<url-pattern>*.do</url-pattern>

</servlet-mapping>

一个该模式的请求URI符合如下格式:

http://www.my_site_name.com/mycontext/actionName.do

上述的映射叫做扩展映射,你也可以声明路径映射,使用/*结尾的模式,如下所示:

<servlet-mapping>

<servlet-name>action</servlet-name>

<url-pattern>/do/*</url-pattern>

</servlet-mapping>

一个该模式的请求URI符合如下格式:

http://www.my_site_name.com/mycontext/do/action_Name

如上描述的资源逻辑映射允许在配置文件中修改资源映射而不需修改任何应用系统代码。该映射方案也被用于Mulitplexed Resource Mapping。控制器为所有的表示层请求提供了一个集中的访问点。控制器区分每一个提交的请求到RequestProcessor,其轮流发送请求到相关的表单bean进行表单验证,然后发到一个请求处理器以访问模型。这个控制器提供的抽象概念减轻了开发者建立公共应用系统服务的困难,如管理视图、会话及表单数据。开发者支持一个通用机制如错误及异常处理,导航,国际化,数据验证,数据转换等。

在Struts 1.1中,控制器需要在init()方法中读取配置。该配置控制了架构的行为,含有URI到请求处理器的映射如使用ActionMapping配置对象,设置信息资源,提供通过插件访问外部资源等等。事实上,提交请求的处理实际发生在ActionServlet委托所有的输入请求到RequestProcessor的过程中。

发送者对象

RequestProcessor功能是作为发送者,按实例化(或复用)一个请求处理器和一个相应的表单bean来处理请求。表单bean和请求处理器中错误的建立或异常的抛出被RequestProcessor处理,并影响RequestProcessor的视图管理功能。表单bean辅助RequestProcessor保存表单数据和/或准备视图必须的中间模型数据。RequestProcessor使用<action>声明struts-config.xml文件,如下所示,例如特定请求的请求处理器。

<action-mappings>

<action path="/editCustomerProfile"

type="packageName.EditCustomerProfileAction"

name="customerProfileForm"

scope="request"/>

</action-mappings>

<form-bean name="customerProfileForm"

type="packageName.customerProfileForm"/>

全部提交的请求都被控制器委托到RequestProcessor的对象发送者。RequestProcessor检查请求URI找到动作标示符,并使用ActionMapping对象中的信息建立一个请求处理器实例,然后调用requesthandler.execute(…)方法。请求处理器中execute(…)方法任务是和应用模型互相作用。请求处理器根据结果返回以一个ActionForward配置对象到RequestProcessor。RequestProcessor将用ActionForward对象来通过执行RequestDispatcher.forward(…)或response.sendRedirect(…)来调用下一个视图。

利用ActionMapping的命令模式

Struts提供一个公开的基于XML语句的方法来说明请求URI中servlet路径与适当的请求处理器之间的映射。这个实现与命令模式[Gof]很相似。以下片断摘自struts-config.xml文件,下列声明用于建立ActionMapping配置对象,它是<action>元素的运行时表现。

<action-mappings>

<action path="/editCustomerProfile"

type="packageName.EditCustomerProfileAction"

name="customerProfileForm"

scope="request"/>

</action-mappings>

以下简要说明上述声明中用到的属性。

path:HTTP请求中虚拟目录的相对路径,用于识别这个动作映射。

type:类名,将用于在处理这个请求的时候建立一个请求处理器实例。

name:JavaBean的逻辑名称,也叫做表单bean,将用于保存表单数据。表单bean将用这个名称保存在指定的范围(scope)中。

scope:保存bean时用请求或会话范围。

上例中Path属性映射到HTML文件中<form>元素的action属性。在上面范例中避免了硬代码映射到代码内部而且可以方便的看到HTML表单中的servlet路径如何映射到请求处理器的实例。另外,应用系统行为和导航语义可以方便的通过修改动作映射来完成。请求处理器是Struts提供的Action类的子类。

对于HTML<form>标签,使用自定义的org.apache.struts.taglib.html.FormTag为action属性动态生成动态URL可以保护HTML文档避免修改虚拟目录或<url-pattern>时的大量变动。对于*.do模式的URL,下面的自定义FormTag <html:form action="/editCustomerProfile?customerType=preferred"> 将使用action属性包含的如下服务器相关URL动态生成一个HTML <form>标签:<form action="/contextPath/editCustomerProfile.do?customerType=preferred"/>

使用name属性,行为映射可以声明一个特定的JavaBean ,其特性将保存来自HTTP请求的参数,该JavaBean是ActionFrom类的子类。行为映射声明中的name属性是在特定范围内使用哪个ActionForm类的实例保存的唯一标示。ActionForm子类使用<form-beans>标签声明于struts-config.xml文件中,如下:

<form-bean name="customerProfileForm" type="packageName.customerProfileForm"/>

参阅“获取表单数据”章节以得到关于<form-beans>元素和ActionForm类的更多信息。

模型与请求处理器的相互作用

Action的一个子类是用于作为提交的请求和模型之间的适配器。Action子类,也叫做请求处理器,是为每个请求特别建立的。一个动作最初被RequestProcessor解释,然后轮流实例化一个相应的请求处理器。这个Action类的对象为每个请求特别建立,已经在前面章节中的发送者阐述。请求处理器实现了命令模式[Gof]。一个终端请求在请求URL中封装了欲执行的行为即servlet路径,该路经信息随后被发送者(RequestProcessor)提取以建立一个相应的请求处理器实例。命令模式消除了用户界面(UI)对请求处理器的影响。

基本的Action类提供访问架构相关资源的常用函数以及保存使用其子类的execute(…)方法而产生的错误的方法。该错误随即通过采用定制的org.apache.struts.taglib.html.ErrorsTag,被获取并显示到HTML表单。请求处理器的execute(…)方法应该包含处理请求参数和相关ActionForm的控制流程,它应该封装模型相关语义,并且应该在模型操作结果的基础上提供下一个视图。请求处理器在第一次建立后就被RequestProcessor捕获,随即被设为对其他的提交请求可用;同样的,请求处理器必须不含有用户特定的状态信息;请求处理器也必须同步化访问需要串行访问的资源。对于一个分布式应用,动作类包括与EJB组件中的事务逻辑相关联的控制逻辑且一般采用Business Delegate[Core]对象来实现该目的。事务委托保护请求处理器免于处理访问分布式组件的复杂度。因为访问服务器端组件的逻辑被嵌入到事务委托中,事务委托设计模式促进了请求处理器与服务器端组件的松散耦合。请求处理器由表示层工作的开发者编写,然而事务委托常常由负责建立事务层服务的开发者编写。对于小型非分布式应用,动作类或许包含事务逻辑。当分布式处理不是必需的且事务逻辑被嵌入在请求处理器中时,Data Access Object [Core]可以用于抽象潜在的数据访问实现,它为请求处理器与数据访问层提供了松散的耦合,从而保护表示层避免在整合层中改变实现。请求处理器基本的Action类提供部分方便的方法,请查阅API文档位于:http://jakarta.apache.org/struts/api/index.html。

使用ActionForward导航

ActionForward对象是配置对象。这些配置对象拥有独一无二的标识以允许它们按照有意义的名称如“success”,“failure”等来检索。ActionForward对象封装了向前进的URL路径且被请求处理器用于识别目标视图。ActionForward对象建立自<forward>元素位于struts-config.xml。下面是一个Struts中<forward>元素例子,属于<action>元素范围。

<action path="/editCustomerProfile"

type="packageName.EditCustomerProfileAction"

name="customerProfileForm" scope="request">

<forward name="success" path="/MainMenu.jsp"/>

<forward name="failure" path="/CustomerService.jsp"/>

</action>

基于执行请求处理器的execute(…)方法的结果,当传递一个值匹配指定于<forward>元素中name属性的值的时候,下一个视图可以在execute(…)方法中被开发者用方便的方法org.apache.struts.action.ActionMapping.findForward(…)选择。ActionMapping.findForward(…)方法既从它的本地范围又从全局范围提供一个ActionForward对象,该对象返回至RequestProcessor以RequestDispatcher.forward(…)或response.sendRedirect(…)调用下一个视图。当<forward>元素有redirect=“false”属性或redirect属性不存在的时候,RequestDispatcher.forward(…)被执行;当redirect=“true”是,将调用sendRedirect(…)方法。下例举例说明了redirect属性的用法:

<forward name="success" path="/Catalog.jsp" redirect="true"/>

在struts-config.xml中<controller>元素还提供另一个特性以控制<forward>元素的的name属性如何被解释。该<controller>元素用于联合<action>元素中input属性,如下所示:

<action path="/editCustomerProfile"

type="packageName.EditCustomerProfileAction"

name="customerProfileForm"

scope="request"

input="profile">

<forward name="profile" path="/CustomerProfile.jsp"/>

<forward name="success" path="/MainMenu.jsp"/>

</action>

<controller>

<set-property property="inputForward" value="true"/>

</controller>

上述的<action>元素包含一个input属性及一个向前名称;该前进名称与<forward>中的相同。在上述的<controller>配置中,当ActionForm.validate(…)返回一个非空或非null的ActionErrors对象,RequestProcessor将选择<forward>元素,其name属性与<action>元素的input属性拥有相同的值;除非用子类取代RequestProcessor,该行为是确认遇到错误的标准行为。对下面的<controller>元素声明,当ActionForm.validate(…)返回一个非空或非null的ActionErrors对象,input属性提供一个向前的URL以代替向前发生ActionForward的名称。在inputForward属性不存在时,这是默认的配置。

<controller>

<set-property property="inputForward" value="false"/>

</controller>

前进指向特定的路径,如果路径中没有”/” 就加上。对于前进或重定向,Struts中的URL在内部被RequestProcessor用下面的结构建立。

如果redirect=true, URL建立如/contextPath/path因为HttpServletResponse.sendRedirect(…)中解释URL采用”/”开头相对于servlet容器根目录。

如果redirect=false, URI建立如/path因为ServletContext.getRequestDisptacher(…)采用虚拟目录相关URL。

捕获表单数据

JSP规范提供了一个标准方法以在请求时从JavaBean提取和保存表单数据,使用<jsp:useBean>和<jsp:setProperty>。然而这种方法导致了在表示层和JavaBeans间产生很强的耦合;此外HTML文档创作者不得不明白这种组件并且知道它们在页面中正确地用法。因为JavaBean可以被<jsp:useBean>标签或被另一个服务器组件建立和放置在指定的范围,那样可能在不同的组件共享JavaBean中产生bean的生命周期管理问题。Struts提供一个机制以提取,保存和验证表单数据;同时它克服了<jsp:useBean>和<jsp:setProperty>的缺点。下面是一对新的<action>和<form-bean>元素。

<form-bean name="customerProfileForm" type="packageName.customerProfileForm"/>

<action path="/editCustomerProfile" type="packageName.EditCustomerProfileAction" name="customerProfileForm" scope="request"/>

上述例子映射一个packageName.customerProfileForm类型JavaBean的name = customerProfileForm(唯一标识)到一个<action>元素的name = customerProfileForm;请求处理器被提交请求的路径/editCustomerProfile唯一标识。该表单建立和使用的语义用下面的静态模型举例说明:
图略
首先,我们将浏览这些表单处理语义直到使用简单JavaBeans对象。这些对象作为ActionForm对象被实现。我们随后将讨论使用DynaActionForm对象进行表单处理,可以在请求时支持属性的动态集合。

用FormTag初始化ActionForm对象

本节早前提到,HTML表单中动作URL被映射到一个<action>配置,并轮流被映射到一个<form-bean>配置。FormTag中由action属性制定的URL被FormTag转换为一个在部署描述符中<url-pattern>确定的路径结构URL。对于扩充的映射,这意味着资源扩展和<url-pattern>指定的一样。因此,一个表单URL中 editCustomerProfile?customerType=preferred被转换为/editCustomerProfile.do?customerType=preferred。FormTag调用RequestUtils.createActionForm(…)方法,查找一个ActionFormBean配置对象(ActionFormBean 是<formbean>元素的运行时表现),其名称匹配指定的相应<action>的名称。一个新的ActionForm实例使用<form-bean>元素type属性被建立;当ActionForm实例在指定的活动范围内未找到时,一个新的实例被建立,否则FormTag调用已存在的表单bean的ActionForm.reset(…)方法以清除并准备接受下一个请求的表单数据。该活动范围被<action>元素中scope属性所指定;新的ActionForm实例或已存在的重新初始化的实例被使用name属性保存在指定的活动范围内

用ActionForm保存表单数据

ActionForm派生的对象用于保存请求对象的参数,因此它们和用户紧密联系。一个ActionForm的子类是一个拥有存取HttpServletRequest对象参数属性的方法JavaBean。如果ActionForm对象被FormTag建立,那么在后来的FormTag表单翻译请求中,RequestProcessor将从制定的活动范围访问表单;欲取得的表单被相关的动作映射所标识。RequestProcessor将随后重新安排表单属性,用请求时参数填充表单,随即调用表单对象的validate(…)方法以履行服务器端用户输入验证。仅当ActionMapping对象中validate属性被设为true时,validate(…)方法被调用;这就是默认的行为。request.getParameterValues(parameterName)被用于得到一个String[]对象,它用来表单填充;验证的结果应该是一个ActionErrors对象,用org.apache.struts.taglib.html.ErrorsTag来显示验证错误给用户。ActionForm也可以被用于为当前用户保存即将被一个视图引用的中间模型状态。

一个ActionForm类也可以被RequestProcessor建立。这是发生在已完成向前进到一个URL,该URL为映射到控制器servlet而不是JSP和相应的动作映射指定的表单属性的。在这个情况下,如果没有在指定的活动范围内找到,RequestProcessor将尝试寻找可能导致创建一个新ActionForm对象的表单bean。该ActionForm对象在指定的活动范围内被用<action>元素的name属性找到;当一个表单对象被RequestProcessor找到,它被传递到请求处理器的execute(…)方法。一个ActionForm对象也可以被请求处理器建立。表单对象建立目的是提供中间模型状态给使用请求范围JSP;这将确保对象不会在有效性过期后仍然存在。默认的,所有的表单都被保存为会话范围。会话中表单对象脱离有效性的存在可能导致浪费内存,同样的,请求处理器必须跟踪保存在会话中的表单对象的生命周期。一个好的捕获表单数据的实践是为横跨多用户交互的相关表单用一个单独的表单bean。表单bean也可以在反馈的时候用来储存能够被自定义标签改变的中间模型状态。在视图中标签用法避免结合Java代码,因此要成一个好的任务划分,web生产组主要处理标志,而应用开发组主要处理Java代码。标签因素退出访问中间模型状态的逻辑;当访问嵌套的对象或当通过聚集列举时这个逻辑可能很复杂。

用动态属性建立ActionForm

一个DynaActionForm对象是一个拥有动态属性集合的对象。DynaActionForm扩展了ActionForm,它的用法允许通过在struts-config.xml声明建立一个表单对象,如下:

<form-bean name="logonForm"

type="org.apache.struts.action.DynaActionForm">

<form-property name="username" type="java.lang.String"/>

<form-property name="password" type="java.lang.String"/>

</form-bean>

RequestProcessor用和ActionForm相同的方式建立、填充并验证了DynaActionForm,例如,请求对象的参数被以<form-bean>元素中特定的动态属性填充到DynaActionForm;其他的参数简单类似。

总结

为一个基于请求/响应的HTTP协议实现MVC语义(semantics)需要付出重大的时间和努力。选择一个合适的架构以解决该问题提供一个领先的项目,允许架构师和开发者关注于事务语义而不是整合语义。Struts也提供充足的模型如Struts Validator以声明表单验证,Tiles以聚集合成视图;这些模型增强了架构而且大大简化了设计和开发一个应用的任务。Struts更多的信息和相关配置及安装的说明可以在http://jakarta.apache.org/struts/userGuide/index.html找到。因为Struts开发是一个正在进行的开发,或许就在你读这篇文章的时候,一些实现也会更改,因此,最好的是用发布于http://jakarta.apache.org/struts的版本说明和更新来补足这章的内容。

参考

[Core] Core J2EE Patterns by Deepak Alur et. al. (Prentice Hall, 2001)

[Gof] Design Patterns by Erich Gamma et. al. (Addison-Wesley, 1995)

你可能感兴趣的:(bean,mvc,应用服务器,servlet,struts)