一、
认识
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
的优点来建立可扩展的应用,
Struts
是一个不错的选择。
3
、
Struts
工作原理
MVC即Model-View-Controller的缩写,是一种常用的设计模式。MVC 减弱了业务逻辑接口和数据接口之间的耦合,以及让视图层更富于变化。MVC的工作原理,如下图1所示:
View
(视图)
|
Controller
(控制)
|
Model
(模型)
|
图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所示:
Client
IE or Netscape
|
Controller
ActionServlet
|
Business Logic
Action
|
Model
JavaBean or EJB
|
View
JSP
|
HTTP
Request
|
Action Form
|
HTTP
Response
|
Struts-config.xml
|
图2
|
控制:通过图2大家可以看到有一个XML文件Struts-config.xml,与之相关联的是Controller,在Struts中,承担MVC中Controller角色的是一个Servlet,叫ActionServlet。ActionServlet是一个通用的控制组件。这个控制组件提供了处理所有发送到Struts的HTTP请求的入口点。它截取和分发这些请求到相应的动作类(这些动作类都是Action类的子类)。另外控制组件也负责用相应的请求参数填充 Action From(通常称之为FromBean),并传给动作类(通常称之为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通常称之为FormBean,封装了来自于Client的用户请求信息,如表单信息。Action通常称之为ActionBean,获取从ActionSevlet传来的FormBean,取出FormBean中的相关信息,并做出相关的处理,一般是调用Java Bean或EJB等。
流程:在Struts中,用户的请求一般以*.do作为请求服务名,所有的*.do请求均被指向ActionSevlet,ActionSevlet根据Struts-config.xml中的配置信息,将用户请求封装成一个指定名称的FormBean,并将此FormBean传至指定名称的ActionBean,由ActionBean完成相应的业务操作,如文件操作,数据库操作等。每一个*.do均有对应的FormBean名称和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成功。
关于在其他工具上的配置,如Eclipse 网上有很多资料,自己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、 创建
RegUserForm
点选Jbuilder下的New
àWeb
àActionForm,在下一步中添加Form的属性,然后下一步,选中Add To struts_config.xml,然后Finish; 会自动生成Form文件,并在Struts_config.xml中将你FormBean加进来,生成的FormBean文件如下:
FormBean
:RegUserForm
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中选刚才创建的RegUserForm ,然后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、
如何支持中文
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自带的文档中就有这个例子,请参考:
写一个自己验证类
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日整理