一、 认识Struts
1、 Framework
Framework概念并不是很新了,伴随着软件开发的发展,在多层的软件开发项目中,可重用、易扩展的,而且是经过良好测试的软件组件,越来越为人们所青睐。这意味着人们可以将充裕的时间用来分析、构建业务逻辑的应用上,而非繁杂的代码工程。于是人们将相同类型问题的解决途径进行抽象,抽取成一个应用框架。这也就是我们所说的Framework。
J2EE体系包括 JSP 、 Servlet 、 EJB 、 WEB SERVICE 等多项技术。这些技术的出现给电子商务时代的 WEB 应用开发提供了一个非常有竞争力的选择。怎样把这些技术组合起来,形成一个适应项目需要的稳定架构是项目开发过程中一个非常重要的步骤。
此步骤一般主要由架构设计师完成,设计师将根据项目需求,对J2EE 体系中的各处技术进行筛选取舍,并考虑到开发过程中的角色分工、后期的运行维护,以及系统扩展性等诸多因素,建立系统的架构。
一个成功的软件需要有一个成功的架构,但软件架构的建立是一个复杂而又持续改进的过程,软件开发者们不可能对每个不同的项目做不同的架构,而总是尽量重用以前的架构,或开发出尽量通用的架构方案,Struts 就是其中之一, Struts 是流行的基于 J2EE 的 web 层的架构方案,其他常用的基于 J2EE 的架构方案还有 Turbine 、 RealMothods 等。
2、 Struts 的起源
Struts最早是作为 Apache Jakarta 项目的组成部分问世运做。项目的创立者希望通过对该项目的研究,改进和提高 Java Server Pages (JSPs) 、 Servlet 、标签库以及面向对象的技术水准。当前最高发行版本为 Struts1 .1 ,可以到 http://jakata.apache.org/Struts 下载。
Struts这个名字来源于在建筑和旧式飞机中使用的支持金属架。它的目的是为了帮助你减少在运用 MVC 设计模型来开发 Web 应用的时间。你仍然需要学习和应用该架构,不过它将可以完成其中一些繁重的工作。如果想混合使用 Servlets 和 JSP 的优点来建立可扩展的应用, Stru ts是一个不错的选择 。
3、 Struts 工作原理
MVC即 Model-View-Controller 的缩写,是一种常用的设计模式。 MVC 减弱了业务逻辑接口和数据接口之间的耦合 ,以及让视图层更富于变化。 MVC 的工作原理, 如下图 1 所示:
Struts 是 MVC的一种实现, 它将 Servlet 和 JSP 标记(属于 J2EE 规范)用作实现的一部分。 Struts 继承了MVC 的各项特性,并根据 J2EE 的特点,做了相应的变化与扩展。
Struts有一组相互协作的类(组件)、Serlvet以及jsp tag lib组成。基于struts构架的web应用程序基本上符合JSP Model2的设计标准,可以说是MVC设计模式的一种变化类型。根据上面对framework的描述,我们很容易理解为什么说Struts是一个web framwork,而不仅仅是一些标记库的组合。 但 Struts 也包含了丰富的标记库和独立于该框架工作的实用程序类 。Struts有其自己的控制器(Controller),同时整合了其他的一些技术去实现模型层(Model)和视图层(View)。在模型层,Struts可以很容易的与数据访问技术相结合,包括EJB,JDBC和Object Relation Bridge。在视图层,Struts能够与JSP, Velocity Templates,XSL等等这些表示层组件想结合。
Struts 的工作原理,如下图2 所示:
控制: 通过图2 大家可以看到有一个 XML 文件 Struts-config.xml ,与之相关联的是 Controller ,在 Struts 中,承担 MVC 中 Controller 角色的是一个 Servlet ,叫 ActionServlet 。 ActionServlet 是 一个通用的控制组件。这个控制组件提供了处理所有发送到Struts 的 HTTP 请求的入口点。它截取和分发这些请求到相应的动作类(这些动作类都是 Action 类的子类)。另外控制组件也负责用相应的请求参数填充 Action From(通常称之为 From B ean ) ,并传给动作类 (通常称之为ActionBean ) 。动作类实现核心商业逻辑,它可以访问java bean 或调用 EJB 。最后动作类把控制权传给后续的 JSP 文件,后者生成视图。所有这些控制逻辑利用 Struts-config.xml 文件来配置。
视图: 主要由JSP 生成页面完成视图, Struts 提供 丰富的 JSP 标签库: Html , Bean , Logic , tiles 等, 这 有利于分开表现逻辑和程序逻辑。
模型: 模型以一个或 多 个java bean 的形式存在。这些 bean 分为三 类:Action Form 、 Action 、 JavaBean or EJB 。 Action Form 通常称之为 F or m B ean ,封装了来自于Client 的用户请求信息,如表单信息。 Action 通常称之为 ActionBean ,获取从 ActionSevlet 传来的 F or m B ean ,取出 F or m B ean 中的相关信息,并做出相关的处理,一般是调用Java Bean 或 EJB 等。
流程: 在Struts 中,用户的请求一般以 *.do 作为请求服务名,所有的 *.do 请求均被指向 ActionSevlet , ActionSevlet 根据 Struts-config.xml 中的配置信息,将用户请求封装成一个指定名称的 F or m B ean ,并将此 F or m B ean 传至指定名称的ActionBean ,由 ActionBean 完成相应的业务操作,如文件操作,数据库操作等。每一个 *.do 均有对应的 F or m B ean 名称和ActionBean 名称,这些在 Struts-config.xml 中配置。
核心: Struts的核心是 ActionSevlet , ActionSevlet 的核心是 Struts-config.xml 。
二、 Struts安装配置
1、 Struts的安装比较简单,下面的以 Tomcat 4.1.24 为例,讲述安装过程。
首先请到 http://jakarta.apache.org/Struts 下载Struts ,建议使用 release 版,现在最高版本为 1.1 ,下载后得到的是一个 ZIP 文件。
将ZIP 包解开,可以看到这个目录: lib 和 webapps , webapps 下有一些WAR 文件。
假设你的Tomcat 装在 c:/Tomcat 下,则将那些 WAR 文件拷贝到 C:/Tomcat/webapps ,重新启动Tomcat 即可。
打开浏览器,在地址栏中输入: http://localhost:8080/Struts-example/index.jsp ,若能见到“powered by Struts ”的深蓝色图标,即说明成功了。这是 Struts 自带的一个例子,附有详细的说明文档,可以做为初学者的入门教程。
另外,Struts 还提供了一系统实用对象: XML 处理、通过 Java reflection APIs 自动处理 JavaBeans 属性、国际化的提示和消息等
2、 关于Struts 配置,由于我是采用 Jbuilder9 为开发工具,这里主要说一下 Struts 在 Jbuilder 中配置: Jbuilder 本身自带已经集成了 Struts1.0 ,但目前的开发大部分都是在 Struts1.1 下,所以下面就介绍 Struts1.1 在 Jbuilder 中的配置:
1、下载
jakarta-struts-1.1.zip 包; jakarta-struts-1.1-src.zip 包
2、将 jakarta-struts-1.1.zip 包解压到 C:/JBuilder9/thirdparty 目录下
jakarta-struts-1.1-src.zip包解压到 C:/JBuilder9/extras 目录下
3、启动 jbuilder9
4、配置 Library
(1 )选择菜单 tools/Configure Libraries
(2 )新建一个 Library
(3 )在 New Library Wizard 对话框中,
Name 输入: struts1.1 ,
location可选: User Home ,
Library paths: add 加入 C:/JBuilder9/thirdparty/jakarta-struts-1.1/lib/struts.jar
(4 )确定创建 Library
(5 )选中 struts1.1 ,修改 Library Settings
Class: C:/JBuilder9/thirdparty/jakarta-struts-1.1/lib/struts.jar
Source: C:/JBuilder9/extras/jakarta-struts-1.1-src/src/share
DocumentationC:/JBuilder9/thirdparty/jakarta-struts-1.1/webapps/struts-documentation.war/api
Required Library:空
Framework:在 Framework 下拉框中,选择 struts ,会自动出现 6 个 taglib 。
struts-bean
struts-html
struts-logic
struts-template
struts-tiles
struts-nested
(6 )点击 OK ,创建 Library 成功。
关于在其他工具上的配置,如E clipse 网上有很多资料,自己google 一下就可以了
三、 开发步骤
为了避免流水仗式的称述, 枯燥无味。让你尽早看到自己的成果,这里将引用一个简单的例子,让你去熟悉基于 Struts 的开发步骤:(这里我采用 Jbuilder 开发环境,可以加快开发速度)
1、 实例描述:
一个用户注册系统,用户通过网页输入相关信息:注册ID 号,密码, EMAIL ,若注册成功,则显示注册信息,反之出现注册失败提示信息。
以下是相关文件的部分核心代码和创建步骤
2、 建立一个Web 应用:
首先点选Jbuilder 主菜单 File|New Project 建立一个 StrutsDemo 工程,接着选 File|New 选择 Web 页中的 Web Application 建立 Web 应用程序,这时要在 Jsp/Servlet frameworks 里勾选 Struts1.1(struts1.1) 然后 OK
3、 创建 RegUser Form
点选Jbuilder 下的 New à Web à ActionForm,在下一步中添加 Form 的属性,然后下一步,选中 Add To struts_config.xml ,然后 Finish; 会自动生成 Form 文件,并在 Struts_config.xml 中将你 FormBean 加进来,生成的 FormBean 文件如下:
FormBean: RegUser Form
package strutsDemo;
import org.apache.struts.action.*;
import javax.servlet.http.*;
public class RegUserForm extends ActionForm {
private String account;
private String email;
private String password;
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public ActionErrors validate(ActionMapping actionMapping, HttpServletRequest httpServletRequest) {
/**@todo: finish this method, this is just the skeleton.
这里验证account 与 password 是否为空
*/
ActionErrors errors = new ActionErrors();
// Account must be entered
if ((account == null) || (account.length() < 1)) {
errors.add("ACCOUNT_ERROR",new ActionError("errors.account.required"));
}
// Secret Phrase must be entered
if ((password == null) || (password.length() < 1)) {
errors.add("PASSWORD_ERROR",
newActionError("errors.password.required"));
}
return errors ;
}
public void reset(ActionMapping actionMapping, HttpServletRequest httpServletRequest) {
account = null;
email = null;
password = null;
}
4、 创建 RegUserAction
点选Jbuilder 下的 New à Web à Action ,在下一步中的 FomBean Name 中选刚才创建的 RegUser Form ,然后 Finish ,修改文件如下:
ActionBean: RegUserAction
package strutsDemo;
import org.apache.struts.action.*;
import javax.servlet.http.*;
public class RegUserAction extends Action {
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
ActionErrors errors=new ActionErrors();
RegUserForm regUserForm = (RegUserForm) form;
String account = regUserForm.getAccount();
String password = regUserForm.getPassword();
String email = regUserForm.getEmail();
if(email==null) email="";
//注册信息到数据库 略
if("account".equals(account)) //这里只是静态判断帐号不能为 account
{
errors.add("ACCOUNT_REPEAT", new ActionError("errors account.repeat"));
saveErrors(request, errors);
return mapping.getInputForward();
}
return mapping.findForward("success");
}
}
5、 创建Jsp 文件 reguser.jsp: result.jsp
注册页面: reguser.jsp
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ page contentType="text/html; charset=GBK" %>
<html:html>
<head>
<title>reguser</title>
</head>
<body>
<h1>JBuilder Generated Struts JSP for ActionForm strutsDemo.RegUserForm</h1>
<hr noshade="noshade" />
<html:errors/>
<p>
<html:form action="/regUserAction.do" method="POST">
account:<br><html:text property="account"/>
<br>
password:<br><html:password property="password"/>
<br>
Email:<br><html:text property="email"/>
<br>
<html:submit property="submit" value="Submit"/>
<html:reset value ="Reset"/>
</html:form>
</body>
</html:html>
注册结果页面: result.jsp
<%@ page contentType="text/html; charset=GBK" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html>
<head>
<title>
result
</title>
</head>
<body bgcolor="#ffffff">
<h1>Register Form Results</h1>
<p>Hello <bean:write name="regUserForm" property="account" />,</p>
<p><strong>Your password:</strong> <bean:write name="regUserForm" property="password" /></p>
<p><strong>Your email is:</strong> <bean:write name="regUserForm" property="email" /></p>
</body>
</html>
6、 修改配置文件Struts_config.xml
Struts-config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
<form-beans>
<form-bean name="regUserForm" type="strutsDemo.RegUserForm" />
</form-beans>
<action-mappings>
<action name="regUserForm" type="strutsDemo.RegUserAction" validate="true" input="/reguser.jsp" path="/regUserAction" >
<forward name="success" path="/result.jsp" />
</action>
</action-mappings>
<!-- ========== Message Resources Definitions =====================-->
<message-resources parameter="strutsDemo.ApplicationResources" />
</struts-config>
另外,注意上面配置文件中的
<message-resources parameter="strutsDemo.ApplicationResources" />
所以还要创建一个 ApplicationResources.properties 文件,放到
WEB-INF/classes/strutsDemo 下,一些消息信息可以都放在这个文件里,Struts 通过它来支持国际化, 我们在 ApplicationResources.properties 文件添加:
errors.account.required = account is required.
errors.password.required = password is required.
errors account.repeat = account is repeat
到此,一个基于Struts 框架的程序就完成了,当然只是一个简单的示例,像一些其他的比如通过 validation.xml 信息验证,在这里并没有出现,目的只是让你认识一个基本的开发过程。下面在Jbuilder 里编译打包成 strutsDemo.war ,放到 C:/tomcat/webapps 下,在浏览器里输入 http://localhost:8080/strutsDemo/ reguser.jsp 就可以看到你的结果了
四、 常见问题与解决方法
实践是发现问题和解决问题的最好方法。在开发过程中,也遇到了一些问题,这里就将目前遇到的问题和我自己的解决方法总结出来,作为以后的参考,当然如果还有好的方法的话,欢迎大家加进来,共同学习进步吧!
1、 如何支持中文
2、 如何扩展 validation.xml 验证文件,写自己的验证函数
3、 在写完jsp 页面后,测试运行时,找不 到Form bean 或页面不能显示
4、 对一些类似的操作想放在一起,不想每个操作都写一个Action
5、 strturs_config.xml文件中的 <action> scope 属性的值
6、 <bean:write>标签中 Filter属性问题(想过滤一些其他 struts 没提供的特殊字符)
7、 如何转换 ApplicationResources.properties 文件中中文提示消息问题
8、 saveErrors(HttpServletRequest request,ActionErrors errors)函数使用问题
9、 Struts 分 页问题
关于Struts 支持中文的问题,大多都是写个过滤器在 doFilter 函数里设置字符集
package com.web;
import javax.servlet.*;
import java.io.IOException;
public class EncodeFilter implements Filter {
protected String encoding = null;
protected FilterConfig filterConfig = null;
protected boolean ignore = true;
public void destroy() {
this.encoding = null;
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
if (ignore || (request.getCharacterEncoding() == null)) {
String encoding = selectEncoding(request);
if (encoding != null)
request.setCharacterEncoding(encoding);
}
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
this.encoding = filterConfig.getInitParameter("encoding");
String value = filterConfig.getInitParameter("ignore");
if (value == null)
this.ignore = true;
else if (value.equalsIgnoreCase("true"))
this.ignore = true;
else if (value.equalsIgnoreCase("yes"))
this.ignore = true;
else
this.ignore = false;
}
}
然后,在web.xml 文件配置一个过滤器,给出 encoding 和 ignore 设置过滤*.do 就可以了,如下:
<filter>
<filter-name>Set Character Encoding</filter-name>
<filter-class>com.web. EncodeFilter </filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>GB2312</param-value>
</init-param>
<init-param>
<param-name>ignore</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<!-- Filter Mapping -->
<filter-mapping>
<filter-name>Set Character Encoding</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
如何扩展 validation.xml 验证文件,写自己的验证函数
Struts本身已经为我们提供了很多验证规则,这些规则即可以在客户端通过 javascript 验证(可以参考 validation-rules.xml 文件),也可以在服务器端验证(参考 struts 源代码中 org.apache.struts.validator . FieldChecks 类)。但有些时候,比较特殊的验证我们就需要自己写验证规则了,比如验证两个字段值是否相等,在会员注册中经常用的就是输入密码和确认密码一致。在Struts 自带的文档中就有这个例子,请参考:
http://localhost:8080/struts-documentation/userGuide/dev_validator.html
写一个自己验证类
public class CustomValidator {
public CustomValidator() {
super();
}
public static boolean validateTwoFields(Object bean,ValidatorAction va,Field field,
ActionErrors errors,
HttpServletRequest request) {
String value =ValidatorUtil.getValueAsString(bean, field.getProperty());
String sProperty2 = field.getVarValue("secondProperty");
String value2 = ValidatorUtil.getValueAsString(bean, sProperty2);
if (!GenericValidator.isBlankOrNull(value)) {
try {
if (!value.equals(value2)) {
errors.add(field.getKey(),
Resources.getActionError(request, va, field));
return false;
}
} catch (Exception e) {
errors.add(field.getKey(),
Resources.getActionError(request, va, field));
return false;
}
}
return true;
}
}
然后在validation.xml 文件里声明如下
<global>
<!-- Custom Validator -->
<validator name="twofields"
classname="CustomValidator"
method="validateTwoFields"
methodParams="java.lang.Object,
org.apache.commons.validator.ValidatorAction,
org.apache.commons.validator.Field,
org.apache.struts.action.ActionErrors,
javax.servlet.http.HttpServletRequest"
msg="errors.twofields" />
</global>
剩下的就是像其他规则一样在Validation.xml 文件里添加你想验证 FormBean 的属性了
在写完jsp 页面后,测试运行时,找不到 Form bean 或页面不能显示
在struts 中,当你想测试你的页面能否正常显示时,要确保你的 formBean 和对应的 Action 正确,否则,页面会不能正常显示 , 出现异常。所以最好先写 From 和 Action, (当然 Action 可以只是一个空的)
对一些类似的操作想放在一起,不想每个操作都写一个Action
对于一些类似的操作,可以将每个Action 操作写在一个 DispatchAction 中,定义不同的操作,配置 Struts_config.xml 文件中的 <action></action> 加入 parameter 属性 , 如下:
<action parameter="method" type="com.web.action.BaoYangListAction" scope="request" path="/qcby/baoYangListAction">
<forward name="baoYangList" path="/qcby/qcby_list.jsp" />
</action>
在页面调用时为"/qcby/baoYangListAction.do?method=****" 其中 **** 为 action 定义的函数名称,这样的好处就是避免了每个操作都要写一个 Action ;
strturs_config.xml文件中的 <action> scope 属性的值
在配置strturs_config.xml 文件中的 <action> 时,要注意 scope 的值,默认的是 "session" ,所以一般我们会将 scope 设置为 "request"
<bean:write>标签中 Filter 属性问题(想过滤一些其他 struts 没提供的特殊字符)
在使用<bean:write> 的标签时,默认的 fitler 属性是 true,( 用于过滤 html 的 <>, 空格等 ) ,对于一些其他的特殊字符转换,比如回车 <br> ,我们可以自己写一个过滤函数将回车转换包含进去,通过自己的程序过滤后,传给 jsp 页面显示,这时要记得
设置<bean:write fitler="false"> ,参考 struts 源代码中的
org.apache.struts.util . ResponseUtils 类的filter() 函数,这就是 Struts 中用在 <bean:write> 标签的过滤函数
如何转换 ApplicationResources.properties 文件中中文提示消息问题
可以先写在 ApplicationResources.properties 文件里采用中文书写信息,然后再用jdk/bin 目录下的 native2ascii 工具,来转换字符,如下:
native2ascii -encoding gb2312 ApplicationResources.properties
ApplicationResources _zh .properties
saveErrors(HttpServletRequest request,ActionErrors errors)函数使用问题
在action 中如果有错误消息要返回到页面,一般采用
saveErrors(HttpServletRequest request,ActionErrors errors)函数,在页面中调用 <html:errors/> 就可以显示到页面,但我在使用以上函数时会出现异常(主要是一些过滤方面的异常),所以采用其原代码那样用 request.setAttribute(Globals.ERROR_KEY, errors) 就可以
Struts 分页问题
关于Struts 的分页,目前解决方法主要就是通过 <logic:iterate >来实现,基本思想就是
写一个分页器( 一个 Bean) 主要用来记录当前的页面,记录数,和页面的大小等属性,
jsp页面传给 Action 查询条件,主要包在 QueryFormBean 中,另外还要有第几页。在 Action 中创建一个指定页面大小的分页器,然后从后台取数据,并通过计算填充分页器的其他属性(如总记录数,当前页面等)同时截取要显示的记录放到一个 Collection 中,将分页器和 Collection 通过 request.setAttribute() 返回给要显示的页面,然后用 <logic:iterate >和 <bean:write> 将数据显示出来就可以了。
An LianWu ( swimren@163.net )
2003-12-10日整理