STRUTS入门教学(4)
|
|
|
作者:爱心 来源:爱心 加入时间:2006年05月22日 |
|
STRUTS入门教学(4) 站点:爱心种子小博士 关键字:STRUTS入门教学(4)
|
STRUTS 入门教学(4)
STRUTS 入门教学(4)
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体系结构中的固有特性,或者是一个值得仿效的方法。(未完待续) |
|
|
|
|
|
|