Stepping through Jakarta Struts(Struts入门)
What is Struts(什么是Struts?)
Struts (来自Jakarta 项目)是一种Java servlet 应用开发架构。
它的目的在于:建立基于Model-View-Controller (MVC)结构的应用。
本教程内容:首先建立一个单页面的示范应用(包括表单),并在此基础上扩充介绍相关知识。
- Figure 1: The MVC architecture –
Struts本身包括用于程序控制的servlet, beans 和用于JSP的标签库,以及将这些“粘”到一起的配置文件。
如果你在构建一个简单的系统,你可能会觉得,要生成和管理这么多文件简直是过分。你甚至可能希望将所有的代码放到一个JSP页面中。
我的建议是:不要这样做!!
Our first Struts application(第一个Struts应用)
- Figure 2: The first, simple application –
本应用第一次访问的时候显示如上所示页面;当输入数据并提交的时候,输入的数据将再次显示在相应字段中(下面将HTML也叫做“控制”)。
Step 0: Downloading and installing Struts(步骤0:下载和安装Struts)
下载地址:http://jakarta.apache.org/struts/.
最简单的安装方法是:将一个名为"struts-blank.war"的小应用拷贝到你的Servlet容器中(对Tomcat来说,就是拷贝到"webapps"目录中)。然后重新启动服务器。
对Tomcat, 用下面地址检查你的安装:
http://localhost:8080/struts-blank/index.jsp
你还可以安装"struts-documentation.war"应用,它包含了许多文档,包括“用户指南” 。
Step 1: The directories and files of a Struts application(步骤1:Struts应用的目录和文件结构)
通过"struts-blank" 应用,你可以知道Struts应用的目录结构:
- Figure 3: The Struts directory structure –
File or Directory name | Purpose |
META-INF | meta信息 |
WEB-INF/classes | Java classes. |
WEB-INF/classes/ApplicationResources.properties | 固定文本、错误信息等 |
WEB-INF/lib/struts.jar | Struts servlet, helper classes, taglib code etc. |
WEB-INF/*.tld | The Struts tag libraries. |
WEB-INF/struts-config.xml | Struts configuration file. |
WEB-INF/web.xml | servlet container的一般配置 |
index.jsp | The jsp-files (and html-files)可以放到根目录 |
- Table 1: The files and directories in a Struts application -
Step 2: Starting on a new Struts application(步骤2:启动一个新的Struts应用)
启动新应用的最简单方法是:拷贝现有的"struts-blank"到你自己的目录,例如目录"myproject",然后修改配置文件。
Step 3: 配置文件:web.xml
将web.xml精简以后,得到如下的内容:
<?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>
<!-- Standard Action Servlet Configuration (with debugging) --> <servlet> <servlet-name>action</servlet-name> <servlet-class> org.apache.struts.action.ActionServlet </servlet-class> <init-param> <param-name>application</param-name> <param-value>ApplicationResources</param-value> </init-param> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</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>2</load-on-startup> </servlet>
<!-- Standard Action Servlet Mapping --> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern>
</servlet-mapping>
<!-- Struts Tag Library Descriptors --> <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-html.tld</taglib-uri> <taglib-location>/WEB-INF/struts-html.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> |
- Figure 4: The web.xml file –
该文件分为三个部分:
- Struts servlet定义部分:"ActionServlet" (只是表单提交后要执行的servlet的基类)
- 调用该servlet的URL映射 (即真正要调用的URL)
- Struts tag libraries标签库的定义
你可以看到,当浏览器请求一个<some-name>.do文件的时候,将调用用户编写的ActionServlet(参见上面着重显示部分)。
现在,我们在上面例子中的表单里,将action-name设为 "submit.do",意思是,当表单提交动作为submit.do 的时候,系统将调用用户编写的逻辑名为submit的动作servlet(该逻辑名到底对应那个class,下面讲)。
Step 4: 配置文件 struts-config.xml
现在需要一些附加的说明:servlet怎样为我们提供服务?我们又如何下达特定的请求指令?
观察一下图1,注意标记为2和3的箭头,他们表示servlet容器和模式之间的作用,包括事务逻辑。
- 首先,Struts servlet自动将表单中的数据提交给一个JavaBean;该bean叫做ActionForm bean, 因为你的bean 必须extend Struts "ActionForm" class。 你可以将这个bean看成一个浏览器和数据库之间的缓冲区,因为它包含了所有表单字段(控制)的get和set方法。它还可以用来初始化表单控制或者验证用户输入。
- 然后,Struts servlet调用Action class。 该class可以使用上面说的ActionForm bean中的数据。 这个Action class就是你的应用的核心代码所在。它运行完毕后,将控制权交回Struts servlet.
- Figure 5: The Action and ActionForm classes –
请求 ( <some-name>.do) 到特定的Action和 ActionForm class的映射在struts-config.xml 中指定。下面是修改后的文件范例:
<?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>
<!-- ========== Form Bean Definitions ================= --> <form-beans>
<form-bean name="submitForm" type="hansen.playground.SubmitForm"/>
</form-beans>
<!-- ========== Action Mapping Definitions ============ --> <action-mappings>
<action path="/submit" type="hansen.playground.SubmitAction" name="submitForm" input="/submit.jsp" scope="request"> <forward name="success" path="/submit.jsp"/> <forward name="failure" path="/submit.jsp"/> </action>
</action-mappings>
</struts-config> |
- Figure 6: The struts-config.xml file –
如上,该文件包含两个部分:
n form-beans部分:列出ActionForm beans;
n action-mappings:提交动作要执行的真正的类的URL映射等
- form-beans部分指定ActionForm bean的逻辑名(该名在action-mapping中被引用)和对应的class文件的路径。在本例中是:逻辑名:"submitForm",type(对应的类)为"hansen.playground.SubmitForm"(还没有忘记吧,ActionForm bean实际上是表单到servelet的一个桥梁,或缓冲区)。
- action-mappings包括:
path - 请求的名称;在我们的例子中,页面中是"/submit.do",这里不要加.do,为"/submit"
type - Action class 的路径
name - ActionForm bean的逻辑名 (即该类的数据要从这个form-bean得来; 因为不同的ActionForm bean对应不同的页面,所以name实际是要指明这个action是那个页面的action)
input - 校验错误将显示在该页。
scope - form bean的生存范围;你也可指定为 "session"
下面的两个forward 标签,用来告诉servlet当收到Action class的"success" 或 "failure" 消息时,分别跳转到哪个页面。
使用合乎惯例的类名是一个好习惯。下面是一个建议:
Class | Actual name |
ActionForm | <action>Form, 其中<action> 是action的路径名 |
Action | <action>Action |
现在可以编码ActionForm 和Action classes以及jsp 视图了。
Step 5: 构造JSP页面
为了使ActionForm bean能够工作,你必须使用Struts自己的标签来构造HTML表单。(这些标签和普通的HTML标签很类似)
submit.jsp源码(它生成图2的表单):
<%@ 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> <head><title>Submit example</title></head> <body>
<h3>Example Submit Page</h3>
<html:errors/>
<html:form action="submit.do"> Last Name: <html:text property="lastName"/><br> Address: <html:textarea property="address"/><br> Sex: <html:radio property="sex" value="M"/>Male <html:radio property="sex" value="F"/>Female<br> Married: <html:checkbox property="married"/><br> Age: <html:select property="age"> <html:option value="a">0-19</html:option> <html:option value="b">20-49</html:option> <html:option value="c">50-</html:option> </html:select><br> <html:submit/> </html:form>
</body> </html> |
- Figure 7: The submit.jsp file –
本例中,我们只用到了html标签库(虽然引用了包括bean和logic三个标签库)
html:errors用于显示校验错误
注意:Struts标签使用"property"而不是HTML惯用的"name"表示标签名. (其他大部分属性名称与传统的HTML一致)
Step 6: 编写 ActionForm 类
该类必须extend Struts ActionForm, 而且必须包含jsp表单中所有的控制 (在我们的例子中是5个)的getter和setter函数(先告诉你一个秘诀:使用JBuilder可以将所有这些简化到让你吃惊的地步;以后再说)。 它还可以包含一个可选的校验函数。注意下面我提供了"lastName" 字段的默认值.
package hansen.playground;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.*;
public final class SubmitForm extends ActionForm {
/* Last Name */
private String lastName = "Hansen"; // default value
public String getLastName() {
return (this.lastName);
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
/* Address */
private String address = null;
public String getAddress() {
return (this.address);
}
public void setAddress(String address) {
this.address = address;
}
/* Sex */
private String sex = null;
public String getSex() {
return (this.sex);
}
public void setSex(String sex) {
this.sex = sex;
}
/* Married status */
private String married = null;
public String getMarried() {
return (this.married);
}
public void setMarried(String married) {
this.married = married;
}
/* Age */
private String age = null;
public String getAge() {
return (this.age);
}
public void setAge(String age) {
this.age = age;
}
}
|
该class 要放到WEB-INF/classes/hansen/playground目录(它的package名称要求这样做)。
Step 7: 编写Action 类
从应用的编程人员的角度看, Action class是应用的核心。但一般说来,它还应该尽量的“瘦”,将事务逻辑放到其他的bean中,甚至其他服务器的EJB中。
Action class必须包含一个"perform"方法。下面是一个最简单的例子,它基本上对请求不加修改的转交出去:
package hansen.playground;
import javax.servlet.http.*;
import org.apache.struts.action.*;
public final class SubmitAction extends Action {
public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) {
SubmitForm f = (SubmitForm) form; //获取form bean
// 取出“姓”(lastName) String lastName = f.getLastName();
// 转换成大写,然后保存到request 对象 request.setAttribute("lastName", lastName.toUpperCase());
// 将控制递交给 success 目标 return (mapping.findForward("success"));
}
} |
提醒:你可能还记得,"success"对象在Struts配置文件中设为submit.jsp
该文件放到WEB-INF/classes/hansen/playground
重要: Struts只创建一个Action class实例,并在应用之间共享它。因此一定要使用局部变量,不要使用成员变量。
Step 8: 测试你的应用
重启servlet container(Tomcat)(任何时候你修改了配置文件或者更新了java 类的时候都需要重启。).
访问:
http://localhost:8080/myproject/submit.jsp
一切顺利的话,你会看到 "Last Name" 被填上 "Hansen"
这意味着, Struts 已经创建了一个ActionForm bean实例,并从中得到了数据。
现在在字段中填上数据并点击提交按钮。你会看到URL现在变成了submit.do但是表单中的数据仍然保留着。
使用标签库(tag libraries)
在 submit.jsp 文件尾部,增加如下代码:
<logic:present name="lastName" scope="request">
Hello
<logic:equal name="submitForm" property="age" value="a">
young
</logic:equal>
<logic:equal name="submitForm" property="age" value="c">
old
</logic:equal>
<bean:write name="lastName" scope="request"/>
</logic:present>
|
标签的意义如下表:
Tags | Purpose |
<logic:present name="lastName" scope="request"> . . . </logic:present> | Only if the name "lastName" is present in the request object we'll evaluate what's inside the opening and closing tags. Therefore nothing will show up until the "perform" method is called. |
<logic:equal name="submitForm" property="age" value="a"> young </logic:equal> | If the property "age" in the ActionForm bean has the value "a" (age 0-19) the text "young" is send to the browser. |
<bean:write name="lastName" scope="request"/> | Sends the value of the "lastName" attribute in the request object to the browser. |
下面是新的页面在你输入数据的时候可能出现的页面情况:
- Figure 8: Output from Struts tags -
校验输入
假定用户必须输入last name, address, sex , age, 最简单的实现是在ActionForm bean中进行校验,但是你也可以在Action class中进行校验。
Step a: 在 ActionForm中编写"validate"方法
下面是要修改的代码;主要里面还增加了日志功能,以记录用户到底都曾在我们的表单你输入了些什么。
public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request) {
// Log the forms data
servlet.log("Lastname:" + lastName);
servlet.log("Address:" + address);
servlet.log("Sex:" + sex);
servlet.log("Married:" + married);
servlet.log("Age:" + age);
// Check for mandatory data
ActionErrors errors = new ActionErrors();
if (lastName == null || lastName.equals("")) {
errors.add("Last Name", new ActionError("error.lastName"));
}
if (address == null || address.equals("")) {
errors.add("Address", new ActionError("error.address"));
}
if (sex == null || sex.equals("")) {
errors.add("Sex", new ActionError("error.sex"));
}
if (age == null || age.equals("")) {
errors.add("Age", new ActionError("error.age"));
}
return errors;
}
servlet 控制器会检查返回的ActionErrors是否为空;如果不为空,控制器将会把控制权交给由配置文件中input参数指定的页面。(我们的例子中是submit.jsp page)
Step b: 定义错误信息
错误信息取自ApplicationResources.properties文件。这些信息使用Struts的<html:errors/>插入到jsp页面中。为了美观,你还应该定义errors.header 和 errors.footer键值。
我们的ApplicationResources.properties文件内容 (如下所是你可以使用 HTML 标签):
errors.header=<h4>Validation Error(s)</h4><ul> errors.footer=</ul><hr>
error.lastName=<li>Enter your last name error.address=<li>Enter your address error.sex=<li>Enter your sex error.age=<li>Enter your age |
- Figure 9: The ApplicationResources.properties file –
现在重新启动服务器。
Step c: 测试校验过程
如果我们不输入数据的话,页面响应:
- Figure 10: Display of validation errors –
Note that age 0-19 is selected as default according to normal HTML selection list practice.
Struts的总体架构
- Figure 11: The Struts components -
最后的提醒
There's a lot more to say about Struts, but having kept my example simple I hope that I have brought forward the basic architecture of Struts. A very important characteristic of this architecture is the loose coupling (loose couple意为“松散耦合”)between modules. There are no hardcoded file names or class names in the controller or in the Action or ActionForm classes.
Another thing is that all text in your pages may be placed in the ApplicationResources.properties file--you may even implement multi-language support(多语言支持) using these properties-files.
In my next article I'll dig somewhat deeper into Struts and build a more realistic application that'll keep track of your CD/Video/DVD collection.
其它参考
There are a lot of good resources available from the Struts web site. My favorites are these:
- Introduction to Jakarta Struts Framework by Sue Spielman: article 1 - article 2 - article 3
- Struts Tutorial by Stephan Wiesner
- Using Struts by Larry Maturo (pdf file)
- The Struts Kickstart FAQ
- and of course: The Jakarta Struts User Guide