Struts,MVC 的一种开放源码实现(4)再访邮件列表样例
 
- UID
- 1066743
|

Struts,MVC 的一种开放源码实现(4)再访邮件列表样例
再访邮件列表样例下面我们看一下 Struts 是如何解决困扰 join.jsp 的这些问题的。改写后的方案由两个项目组成。第一个项目包含应用程序的逻辑部分,这个应用程序是独立于Web 应用程序的。这个独立层可能是用 EJB技术实现的公共服务层。为了便于说明,我使用 Ant构建进程创建了一个称为 business 的包。有几个原因促使我们使用独立的业务层:
- 划分责任
单独的包使管理人员能够在开发小组内委派责任。这也有助于提高开发人员的责任心。 - 通用件
我们设想开发人员将这个包看作一个商业软件。将它放在另外的包中使它更像通用件。这个包可能是通用件,也可能是由组织内部的另一个小组开发的。 - 避免不必要的构建和单元测试。
分开的构建进程有助于避免不必要的构建和单元测试。 - 使用接口开发
在进行开发和避免不必要的耦合时,它有助于从接口的观点来思考问题。这是极重要的一个方面。当开发您自己的业务包时,这些业务类不应该关心到底是Web应用程序执行调用,还是独立应用程序执行调用。因此,应该避免在业务逻辑层使用对servlet API 或 Struts API 调用的任何引用。 - 稳定性
并不是每个组织都每天、每周甚至每月进行检修。因此,在进行开发时,稳定的接口点是重要的。不能因为业务包处于变迁阶段就认为Web 项目也应该处于变迁阶段。 业务构建注释我用 Ant 构建项目,并用 JUnit 运行单元测试。business.zip包含构建业务项目所需的一切,当然 Ant 和 JUnit除外。这个包脚本将构建类,运行单元测试,创建 Java 文档和 jar文件,最后将所有这些内容压缩到一个 zip 文件中发送给客户。只要对 build.xml 作一些修改,您就可以将它部署到其他平台上。 Business.jar 位于 Web 的下载部分,因此,您并非必须下载并构建这个业务包。
Web 项目第二个项目是用 Struts 开发的一个 Web 应用程序。您将需要一个符合 JSP1.1 和 Servlet 2.2 规范的容器。最快的入门方法是下载并安装 Tomcat3.2(请参阅 )。直到有 Struts 的1.0 发行版之前,我建议您从 Jakarta 项目获得最新的版本(请参阅 )。这对我来说是个大问题,我不能确保我的Web 项目样例能与您下载的 Struts 一起工作。Struts仍在不断变化,所以我不得不经常更新我的项目。在本项目中,我使用的是jakarta-struts-20010105.zip。图 8 显示了此 Web项目的结构。如果您已安装了 Ant,则运行这个版本将创建一个称为 joinStruts.war 的 war文件,您随时可以部署这个文件。
图 8. Web 项目的结构 清单 4 显示了转换后的 JSP 文件,称为 joinMVC.jsp 。这个文件从最初的 50 行变为 19 行,并且现在不含任何 Java 代码。从网页设计人员的角度来看,这是个巨大的改进。
清单 4. joinMVC.jsp -- 再访简单的 JSP 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| <%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts.tld" prefix="struts" %>
<%@ taglib uri="/WEB-INF/struts-form.tld" prefix="form" %>
<html>
<head>
<title><struts:message key="join.title"/></title>
</head>
<body bgcolor="white">
<form:errors/>
<h3>Enter your email to join the group</h3>
<form:form action="join.do" focus="email" >
<form:text property="email" size="30" maxlength="30"/>
<form:submit property="submit" value="Submit"/>
</form:form>
</body>
</html>
|
网页的变化下面是使用 Struts 标记库之后所发生变化的列表:
- Import
1
| <%@ taglib uri="/WEB-INF/struts.tld" prefix="struts" %>
|
用于 Java 代码的 <%@page import? 已被替换为用于Struts 标记库的 <%@ taglib uri? 。 - 文本
1
| <struts:message key="join.title"/>
|
资源属性文件包含 join.title 的文本。在本例中,ApplicationResources属性文件包含这个名值对。这使字符串更易于查看和国际化。 - 错误
ActionServlet 或 ActionForm 构建要显示的错误消息。这些错误消息也可以包含在属性文件中。ApplicationResources也提供了一种格式化错误的方法,即设置 error.header 和 error.footer 。 - HTML 表单
1
| <form:form action="join.do" focus="email" >
|
- JSP <form> 标记和属性替代了 HTML <form> 标记和属性。 <formaction="join.jsp" name="join"> 已更改为 <form:form action="join.do" focus="email"> 。
- HTML <input> 标记已替换为 <form:text/> 。
- HTML <submit> 标记已替换为 <form:submit/> 。
模型 -- 会话状态JoinForm 扩展了 ActionForm 并包含表单数据。本例中的表单数据只有电子邮件地址。我已为电子邮件地址添加了一个写方法和读方法,以供框架访问。为了便于说明,我重写了 validate() 方法,并使用了 Struts 的跟踪功能。Struts将创建 JoinForm 并设置状态信息。
模型 -- 业务逻辑如前所述, Action 是控制器和实际业务对象之间的接口。 JoinAction 包装了对 business.jar 的调用,这些调用最初在 join.jsp 文件中。 JoinAction 的 perform() 方法在清单 5 中列表。
清单 5. - JoinAction.perform()1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
// 抽取我们将会用到的属性和参数
JoinForm joinForm = (JoinForm) form;
String email = joinForm.getEmail();
ActionErrors errors = new ActionErrors();
// 存储输入....
try {
business.db.MailingList.AddEmail(email);
} catch (Exception e) {
// 记录日志,打印栈
// 将错误回显给用户
errors.add("email",new ActionError("error.mailing.db.add"));
}
// 如需任何消息,请将指定的错误消息键保存到
// HTTP 请求中,以供 <struts:errors> 标记使用。
if (!errors.empty()) {
saveErrors(request, errors);
// 返回到初始表单
return (new ActionForward(mapping.getInput()));
}
// 将控制权转交给 Action.xml 中指定的 'success' URI
return (mapping.findForward("success"));
}
|
注: perform() 返回一个称为 ActionForward 的类,该类通知控制器下一步该执行什么操作。在本例中,我使用从控制器传入的映射来决定下一步的操作。
控制器我已修改了 JSP文件,并创建了两个新类:一个类用来包含表单数据,一个类用来调用业务包。最后,我通过修改配置文件 struts-config.xml 将它们整合起来。清单 6显示了我添加的 action 元素,这个元素用来控制 joinMVC.jsp 的流程。
清单 6. Action 配置1
2
3
4
5
6
7
8
| <action path="/join"
name="joinForm"
type="web.mailinglist.JoinAction"
scope="request"
input="/joinMVC.jsp"
validate="true">
<forward name="success" path="/welcome.html"/>
</action>
|
action 元素描述了从请求路径到相应的 Action 类的映射,应该用这些类来处理来自这个路径的请求。每个请求类型都应该有相应的 action 元素,用来描述如何处理该请求。对于 join 请求:
- joinForm 用来容纳表单数据。
- 因为 validate 被标记为 true,所以 joinForm 将试图进行自我验证。
- web.mailinglist.JoinAction 是用来处理对这个映射的请求的 action 类。
- 如果一切顺利,该请求将转到 welcome.jsp 。
- 如果出现业务逻辑故障,流程将返回到 joinMVC.jsp ,这是最初发出请求的网页。为什么会这样呢?在清单6 的 action 元素中,有一个称为 input 的属性,其值为 "/joinMVC.jsp" 。在我的 JoinAction.perform() (如清单 5所示)中,如果业务逻辑失败, perform() 就返回一个 ActionForward ,并以 mapping.getInput() 作为参数。本例中的 getInput() 是 "/joinMVC.jsp" 。如果业务逻辑失败,它将返回到 joinMVC.jsp ,这是最初发出请求的网页。
|
|
|
|
|
|