Java 版 (精华区)
发信人: DreamWeaver (猪头小队长·@^oo^@), 信区: Java
标 题: 用Struts Framework开发应用程序
发信站: 哈工大紫丁香 (2003年07月24日19:50:03 星期四), 站内信件
http://www.zdnet.com.cn/developer/tech/story/0,2000081602,39035234,00.htm
用Struts Framework开发应用程序
作者: BUILDER.COM
Thursday, May 23 2002 2:15 PM
------------------------------------------------------------
Struts Framework是一种基于Java的技术,Web应用程序开发人员通过Struts
Framework即可充分利用面向对象设计、代码重用以及“编写一次、到处运行”
的优点。Struts提供了一种创建Web应用程序的框架,其中对应用程序的显示、
表示和数据的后端代码进行了抽象。
Struts最早于2002年5月作为Jakarta项目的组成部分问世,Jakata项目由Apache
Foundation.运做。项目的创立者希望改进和提高Java Server Pages (JSPs)、
servlet、标签库以及面向对象的技术水准。在钻研Struts之前你应该
比较熟悉JSP、Java编程和一定的设计模式,尤其应该了解MVC(Model-View-Co
ntroller:模型、视图和控制器)软件程序架构。学习Struts一开始会比较难
,一旦理解了这一技术你将发现自己掌握了一种开发大规模Web应用程序的设计
利器。
MVC原理
Struts是对MVC设计模式的一种实现。MVC设计模式为构建可扩展、可重用的代
码打下了一个坚实的基础。MVC设计模式最吸引人之处在于它迫使你必须抽象自
己的代码,把项目分解为表示、逻辑和控制三部分,它们的作用正如一个交响
乐团中的音乐、团员和指挥。
MVC设计模式中的“模型”指的是真正完成任务的代码。对大多Web应用程序来
说,功能比界面感觉更重要。在模型同界面相分离的情况下,代码即可实现可
管理性和可重用性。比方说,假如你开发了一个保险应用程序,其中的“模型
”就是处理要求、计算保险费和同数据库交互的那些代码。模型通常也被称做
“业务逻辑”。
所谓的“视图”其实就是界面。在MVC模式下,通常的设计前提是界面任务并不
大。当然,视图应该具有一定的功能性并遵守可用性的约束,但视图(界面)
不应当处理数据。事实上,界面的每一部分都只能包含采集数据的足够逻辑并
把它传递给设计模式中的其他组成部分供处理。从理论上说,如果你在模型中
实现全部业务逻辑那么你可以创建多个视图,也就是桌面应用程序、数据库系
统或者Web应用程序的界面。由于所有最麻烦的工作代码都保存在了模型上,所
以创建新的视图并不令人感到过分的痛苦,产生软件编码错误的可能性也就随
之大大降低了。
“控制器”控制着模型和视图之间的交互过程。它决定着向用户返回怎样的视
图、检查通过界面输入的信息以及选择处理输入信息的模型。
在介绍了MVC之后,现在我们就下载Struts Framework、了解Struts采用MVC设
计技术的原理。
获取Struts Framework
Struts Framework 可以从Jakarta项目的网站上获取。目前可用的代码版本还
是1.0,但该版本是稳定和成熟的版本。同Apache Foundation维护的其他项目
一样,Struts Framework是一种开放源代码软件,所以你可以获得软件源代码
亲自编译程序或者直接下载二进制版本。大多数用户并不需要源代码,但知道
它的存在也不是坏事,说不定以后就用得着。
Struts分派版随带了众多示例,所有这些示例都打包为WAR文件(Web
Application Archive)供部署在应用服务器上。假如你想用Struts创建自己的
应用程序,那么你会在Struts库目录下找到所有你需要的东西。在这个目录下
有个JAR文件包含了所有的框架源代码和标签库的全部定义。在创建新应用程序
的时候需要把这些文件拷到WEB-INF目录下供部署用。将来我们会撰文讨论如何
完成这些工作,目前你仅需要知道它们都放在什么地方就可以了。
MVC和Struts Framework
现在我们就看看Struts Framework是如何采用MVC模式来创建Web应用程序的。
视图
Struts采用JSP作为MVC的视图。每一视图都是采用了定制标签库的JSP页面,这
些定制标签库则由Struts Framework提供。全部表单元素都是用定制标签编码
的,所以这些页面能够很方便地同控制器交互。每一个表单都通过映射JSP到se
rvlet的请求这一方式指向控制器的特定入口点。
控制器
控制器就是Java servlet,由Struts Framework提供,负责视图和模型之间的
交互。控制器的每个入口点都由名为struts-config.xml的配置文件设置。该文
件把来自视图的请求映射为特定的java类以进行相应的处理。控制器还指定下
一个视图的位置。
模型
Java beans就是模型的代表。对进入控制器的每个入口点来说,bean的作用是
存储从视图获取的信息,同时被称为“行动类”一种Java类则被指定为对表单
输入采取行动。行动类(action class)实现了业务逻辑。
以上介绍了Struts Framework同MVC之间的关系,接下来的第二部分则引领读者
用Struts创建应用程序。
本文的上一部分讨论了Struts Framework同MVC设计模式之间的关系。现在你应
该比较清楚Struts是如何同MVC模式各个组成部分打交道的。接下来我们继续讨
论用Struts具体创建新应用程序的问题。
为了用Struts Framework创建新的应用程序,你首先得创建一个新目录树,如
下所示:
MyWebApp/
WEB-INF/
classes/
lib/
然后把struts.jar文件从Struts下载目录下的lib目录拷贝到以上目录树的lib
目录,同时请把所有的标签库描述符(TLD)文件从Struts lib目录拷贝到WEB-
INF目录。
在本文的示例应用程序中,你将创建一个Web应用程序,它通过XML-RPC同Blogg
er网站管理的服务通讯。该应用程序相当简单,它由一个登录页面和提交新数
据到相应服务的页面组成。登录页面会获取用户名和口令,然后把这些信息将
保存在一个Java bean内。
创建web.xml文件
如清单A所示,首先得创建web.xml部署文件,它应该保存在WEB-INF目录下。该
文件说明新创建的Web应用程序同时还负责实例化你的控制器servlet。
清单A:
----------------------------------------------------------------
Listing A: Web XML deployment file
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
----------------------------------------------------------------
如清单B所示,servlet的定义实例化ActionServlet,它就是Struts控制器。
清单B:
----------------------------------------------------------------
Listing B: ActionServlet
<!-- Action Servlet Configuration -->
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>application</param-name>
<param-value>ApplicationResources</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>validate</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
----------------------------------------------------------------
装载servlet之后就需要把请求映射到该servlet,如清单C所示。在本例中,所
有的请求都映射为*.action的形式被传递给Struts控制器。
清单C:
----------------------------------------------------------------
Listing C: Servlet mapping
<!-- Action Servlet Mapping -->
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
----------------------------------------------------------------
在清单D中指定了要装载的默认页面和用到的标签库。
清单D:
----------------------------------------------------------------
Listing D: Tag libraries
<!-- The Welcome File List -->
<welcome-file-list>
<welcome-file>bloggerLogin.jsp</welcome-file>
</welcome-file-list>
<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-bean.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>
</web-app>
----------------------------------------------------------------
在如上所示定义了Web应用程序之后我们再了解下控制器的配置细节。
控制器的配置:struts-config.xml
控制器的配置决定了发给控制器的请求将得到怎样的处理。你不妨阅读下
struts-config.xml文件(如清单E、F和G所示),从中可以看出,所有冠
以*.action的请求都被映射到了控制器。现在你就必须指定对每一请求该
如何处理。
清单E:
----------------------------------------------------------------
Listing E xml-config
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.0//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_0.dtd">
<struts-config>
----------------------------------------------------------------
清单F:
----------------------------------------------------------------
Listing F: form bean
<form-beans>
<form-bean name="bloggerForm" type="xmlrpc.bloggerForm"/>
<form-bean name="bloggerLogin" type="xmlrpc.bloggerUserForm"/>
</form-beans>
----------------------------------------------------------------
清单G:
----------------------------------------------------------------
Listing G: action mapping
<action-mappings>
<action path="/bloggerLogin" type="xmlrpc.bloggerUserAction"
name="bloggerLogin">
<forward name="display" path="/bloggerPost.jsp"/>
<forward name="bloggerPost" path="/bloggerPost.jsp"/>
<forward name="bloggerError" path="/bloggerError.jsp"/>
<forward name="bloggerLogin" path="/bloggerLogin.jsp"/>
</action>
<action path="/blogger" type="xmlrpc.bloggerAction"
name="bloggerForm">
<forward name="display" path="/bloggerPost.jsp"/>
<forward name="bloggerPost" path="/bloggerPost.jsp"/>
<forward name="bloggerError" path="/bloggerError.jsp"/>
<forward name="bloggerLogin" path="/bloggerLogin.jsp"/>
<forward name="bloggerLogin" path="/bloggerLogin.jsp"/>
</action>
</action-mappings>
</struts-config>
----------------------------------------------------------------
就我们的示例应用程序来说,你需要处理登录请求和页面提交的信息。每个视图
都分别具有相应的表单Java bean和用于处理的action类。
配置文件分为两个部分:其一是表单Java bean,负责指定存储来自视图的参数
值以及action映射值的Java类。第一部分(如清单F所示)处理提交的信息,而
第二部分(清单G)则负责处理登录。以后我们主要讨论对表单的处理。
action映射把入站的servlet请求同表单和action类联系了起来。第1步是为登录
过程创建映射。发给服务器的实际请求是页面bloggerLogin.action,但是你只
设置了前半部分。接下来,你还得把所有给bloggerLogin.action的请求都保存
在bloggerLogin表单Java bean内(早就配置为xmlrpc.bloggerUserForm)。
xmlrpc.bloggerUserAction类负责处理这个Java bean。在action标签内的转发
XML元素代表了控制器返回的视图名。每种action类都必须返回下一视图的名字,
这样你就可以把文件名保存在配置文件里了。
控制器就配置好了,下面我们来看看登录视图。
视图用JSP创建,用Struts标签库同表单Java bean交互,意味着所有的表单元
素都必须用Struts标签库来创建。清单H所示为有关的源代码,你可以从中了解
视图的外观。
清单H:
----------------------------------------------------------------
Listing H: creating a view
<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html:html locale="true">
<head>
<title> </title>
</head>
<body bgcolor="#FFFFFF">
<html:errors/>
<html:form action="bloggerLogin.action" focus="username">
<table border="0" width="50%">
<tr>
<td><bean:message key="info.username"/></td>
<td><html:text property="username" size="16"/></td>
</tr> <tr>
<td><bean:message key="info.password"/></td>
<td><html:password property="password" size="16"/></td>
</tr>
<tr>
<td><html:submit property="submit" value="Submit"/></td>
<td><html:reset/></td>
</tr>
</table>
</html:form>
</body>
</html:html>
----------------------------------------------------------------
正如你所看到的那样,除了格式化以外该文件中几乎再无其他内容。事实上,
文档内连修饰性文本都没有,这是因为你已经把所有的文本保存在了一个属性
文件里。在你配置servlet的时候你就会用到一个应用程序资源文件:Applicat
ionResources,该文件会从WEB-INF/classes/ApplicationResources.properti
es装载。该文件就是包含页面文本消息的属性文件。装载ApplicationResource
s.properties有助于创建应用程序的国际化版本。请查看清单I中显示的属性文
件内容。
清单I:
----------------------------------------------------------------
Listing I: properties file
info.username=Username
info.password=Password
info.blogid=Blog Id
info.updateblog=Update Blog by FTP?
----------------------------------------------------------------
接下来我们再回到登录视图。如果你查看视图的HTML代码,你会注意到所有的H
TML标签都是用标签库编码的。这就是Struts调整HTML文档和表单Java bean之
间参数值的方法。在处理每一JSP页面时。标签库会被扩展为HTML,而内部的Ja
va bean则包含默认值。最后出现的页面如图A所示。
图A
http://www.zdnet.com.cn/i/developer/story/200205/39035289/image003.gif
JSP登录页面
然后我们再看看如何创建表单Java bean以捕获来自浏览器的输入信息。
创建表单Java bean
每一个表单Java bean都是一个小小的Java类,其中包含了带设置和提取函数的
属性。对视图中所具有的表单输入而言,表单视图中都具有相应的属性。如果
你回忆下struts-config.xml文件中的代码,你应该记得发给bloggerLogin.act
ion的请求都被发送给了bloggerUserForm,后者就是一个Java bean。清单J即
是bloggerUserForm的代码。现在我们就来了解一下提取(getter)和设置(se
tter)函数如何用于操作属性。
清单J:
----------------------------------------------------------------
Listing J: bloggerUserForm
package xmlrpc;
import org.apache.struts.action.ActionForm;
public class bloggerUserForm extends ActionForm {
protected String username;
protected String password;
public void setUsername(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword() {
return password;
}
public void reset() {
username = null;
password = null;
}
}
----------------------------------------------------------------
实际上,登录视图中用到的两种属性都在Java 类中得到了表示。
为了搞清楚所有以上一切是如何组织起来的,我们再来看看action类以及业务
逻辑的实现原理。
创建action类
action类的任务最繁重。Action类bloggerUserAction读取bloggerUserForm的
值并将其保存到名为bloggerUser的Java bean。为什么要把这些参数值保存在J
ava bean里呢?这是因为今后还需要以不同形式用到这些值。实际上,它们就
是会话过程中的全局参数值。这些信息之后由用户用在请求和视图之间的通讯
过程中。你的action类将利用Blogger服务认证用户并且获取将要修改的有效页
面。该类的代码如清单K所示。
清单K:
----------------------------------------------------------------
Listing K: action class
package xmlrpc;
import java.util.Vector;
import java.util.Hashtable;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ForwardingActionForward;
/* This action performs the login, and passes the user on to */
/* the post screen */
public class bloggerUserAction extends Action {
----------------------------------------------------------------
每一种action类都必须执行一定的方法,这些方法在需要实现某些行为的时候
由控制器servlet执行。如清单L所示。所用到的表单Java bean保存在ActionFo
rm中。
清单L:
----------------------------------------------------------------
Listing L: perform method
public ActionForward perform(ActionMapping mapping,ActionForm form,
HttpServletRequest request,HttpServletResponse response){
HttpSession session = request.getSession();
if (form instanceof bloggerUserForm) {
bloggerUserForm theForm = (bloggerUserForm) form;
----------------------------------------------------------------
你可以调用表单的参数提取方法从表单获取参数值进行处理。通过在请求中设
置属性的方式,你还可以在JSP的表单中设置默认值(参看清单M)。
清单M:
----------------------------------------------------------------
Listing M: getter and setter methods
String username = theForm.getUsername();
String password = theForm.getPassword();
request.setAttribute("username", username);
request.setAttribute("password", password);
----------------------------------------------------------------
在清单N中,你还可以由此检查全局Java bean,也就是bloggerUser是否已经装
载。否则,你可以实例化一个新的。然后给bloggerUser Java bean设置来自表
单的参数值。
清单N:
----------------------------------------------------------------
Listing N: global bean bloggerUser
bloggerUser user = (bloggerUser) session.getAttribute("b_user");
if (user == null) {
user = new bloggerUser();
}
user.setUsername(username);
user.setPassword(password);
----------------------------------------------------------------
现在考察业务逻辑。在清单O中你提出自己的XML-RPC请求并把结果保存在
Java bean内。
清单O:
----------------------------------------------------------------
Listing O: XML-RPC request
//List Vectors to make a userinfo request
Vector params = new Vector();
params.addElement(bloggerUser.APP_KEY);
params.addElement(username);
params.addElement(password);
xmlRequest xmlrpc = new xmlRequest();
xmlrpc.setParams(params);
xmlrpc.setXmlRequestUrl(bloggerUser.blogger_url);
xmlrpc.setMethod("blogger.getUsersBlogs");
try {
Vector result = (Vector) xmlrpc.getResult();
for (int i=0; i < result.size(); i++) {
System.out.println(
(String)((Hashtable) result.get(i)).get("blogid"));
user.addBlog((String)((Hashtable) result.get(i)).get("blogid"));
}
} catch (Exception e) {
ActionErrors errors = new ActionErrors();
errors.add(ActionErrors.GLOBAL_ERROR,new ActionError(
"Blogger Error: " + e));
saveErrors(request, errors);
return mapping.findForward("bloggerError");
}
----------------------------------------------------------------
在清单P,中,你把全局Java bean bloggerUser 保存在b_user会话属性中,
并把该用户信息转发给JSP页面bloggerPost,后者对应struts--onfig.xml文
件中的bloggerPort.jsp。
清单P:
----------------------------------------------------------------
Listing P: global bean in session properties
session.setAttribute("b_user", user);
//return a forward to display.jsp
return mapping.findForward("bloggerPost");
}
//this shouldn't happen in this example
return null;
}
}
----------------------------------------------------------------
然后,从以上代码你就可以了解到MVC模式下一系列的处理过程了,我们看看下
一个视图,了解下如果从视图内同全局Java bean交互。
更智能的视图
视图并不完全排斥逻辑。有时你需要给表单自身增加点“智力”,比如动态创
建表单元素或者显示对某些字段的访问等。清单Q所示bloggerPost.jsp中的表
单在用户成功登录之后返回给用户,它根据先前的XML-RPC请求来动态地创建选
择框。
清单Q:
----------------------------------------------------------------
Listing Q: bloggerPost.jsp
<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic"%>
<html:html locale="true">
<head>
<title> </title>
</head>
<body bgcolor="#FFFFFF">
<html:errors/>
<html:form action="blogger.action" focus="username" scope="session">
<table border="0" width="50%">
----------------------------------------------------------------
按照清单R中的代码所示,你已经捕获了用户输入的用户名和口令值并将其链接
到Java bean b_user。这正是在执行action 类的时候在会话属性的bloggerUse
r Java bean中保存的名字。
清单R:
----------------------------------------------------------------
Listing R: username and password values
<tr>
<td><bean:message key="info.username"/></td>
<td><html:text property="username" name="b_user" size="16"/></td>
</tr>
<tr>
<td><bean:message key="info.password"/></td>
<td><html:password property="password" name="b_user" size="16"/></td>
</tr>
<tr>
<td><bean:message key="info.blogid"/></td>
<td>
----------------------------------------------------------------
你还能通过b_user这个 Java bean 的使用反复迭带授权可编辑的页面。
b_user Java bean具有一个blogs属性,它是一个ID号的ArrayList
(Struts执行b_user的getBlogs()函数获得这个属性)。ArrayList的
各个元素都保存在本地Java bean b_result中,你可以用来作为html:option的
值以创建弹出式菜单,如清单S所示。
清单S:
----------------------------------------------------------------
Listing S: blogs iteration
<html:select property="blogid">
<logic:iterate id="b_result" name="b_user" property="blogs">
<html:option value="<%= (String) b_result%>"/>
</logic:iterate>
</html:select></td>
</tr>
<tr>
<td><bean:message key="info.update"/></td>
<td><html:checkbox property="update"/></td>
</tr>
<tr>
<td colspan=2>
<html:textarea property="content" cols="40" rows="5"/><br>
</td>
</tr>
<tr>
<td><html:submit property="submit" value="Submit"/></td>
<td><html:reset/></td>
</tr>
</table>
</html:form>
</body>
</html:html>
----------------------------------------------------------------
最后显示的页面如图B所示。
图B
http://www.zdnet.com.cn/i/developer/story/200205/39035289/image004.gif
显示页面
以上的这个JSP页面不仅仅只是传递信息;它实际上还具有某些构造页面的逻辑
。你也可以编写其他JSP标签来创建简单或复杂的视图。但是,这里的思想是,
任何对视图的处理只能同表示视图有关。而真正的后台处理或者其他业务逻辑
都不能放在JSP视图之内。
你已经看到,Struts是一种采用MVC模式创建Web应用程序的强大框架。随着你
开发的应用程序变得越发复杂,你最终会发现利用这个好东西照样可以快速创
建出崭新的视图、可重用的组件和Java bean。
<end>
--
○︿
︶\ 面对毫无方向的未来,我只能不断奔跑
√ ﹀ . 。 在我身后,留下的脚印写满遗憾 ..。
※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: 202.118.239.4]
※ 修改:.DreamWeaver 於 Jul 24 19:52:52 2003 修改本文.[FROM: 202.118.250.18]
※ 修改:·DreamWeaver 於 07月24日20:39:27 修改本文·[FROM: 202.118.239.4]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:217.423毫秒