用实例来说明struts1.2.9的流程。
struts开发环境搭建:
从网上下载struts开发包和源代码,然后在struts目录下解压开发包,如下图:
图1
在lib目录中有需要进行开发struts项目的所有jar包,webapps目录下有一个模板空项目struts-blank.war和struts的说明文档struts-documentation.war,分别对他们解压,解压后的情况见上图。
1. 用MyEclipse创建一个web项目struts_login
2. 将STRUTS_HOME(在此处为D:/CommonLibs/struts/struts-1.2.9-bin)/lib中的所有jar包拷贝到刚创建的Web项目的
WebRoot/WEB-INF/lib目录下
3. 将struts-blank项目中的WEB-INF目录下的web.xml和struts-config.xml两个配置文件作为参考模板拷贝到上述新建web项目的WebRoot/WEB-INF/lib目录下,并修改他们,使之类似如下:
修改后的web.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<!--
将启动页面改为login.jsp
-->
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
<!-- Standard Action Servlet Configuration (with debugging) -->
<servlet>
<servlet-name>action</servlet-name>
<!--
指定中央控制servlet,即ActionServlet。这个ActionServlet将截获所有的
请求,然后分发请求到相应的Action,收到Action的返回结果后,再将结果转向
(forward)到相应的页面。
-->
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<!--
下面这个是指定struts-config.xml的位置,struts缺省地就是去找
/WEB-INF/struts-config.xml文件,所以下面的<init-param>部分
其实是可以省略的。
-->
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<!--
指定servlet日志输出等级,可省略。
-->
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<!--
指定apache第三方组件(用于xml的解释和读写)日志输出等级,可省略。
-->
<init-param>
<param-name>detail</param-name>
<param-value>2</param-value>
</init-param>
<!--
指定ActionServlet在应用服务器一启动的时候就初始化,此句宜留下。
-->
<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>
</web-app>
修改后的struts-config.xml文件:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">
<struts-config>
</struts-config>
随后随着项目的进行,需要修改web.xml和struts-config.xml这两个文件。至此,struts开发环境配置完毕。
4. 关联struts源代码文件,以便于开发时进行调试。
图2
双击ActionServlet.class,则右边的编辑框出现一个Attach Source的按钮,如上图。点击Attach Source按钮出现:
图3
点击External File,然后Navigate到struts-1.2.9-src.rar压缩包的位置:
图4
点击打开按钮
图5
点击OK按钮即可。这样就将struts源代码关联上了。
5. 添加三个jsp页面:login.jsp、login_success.jsp和login_error.jsp,它们的代码分别如下:
login.jsp的代码
<%@ page language="java" contentType="text/html; charset=GB18030" pageEncoding="GB18030"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GB18030">
<title>用户登录</title>
</head>
<body>
<h1>用户登录</h1>
<hr>
<form action="login123.do" method="post">
用户:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
login_success.jsp的代码
<%@ page language="java" contentType="text/html; charset=GB18030"
pageEncoding="GB18030"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GB18030">
<title>登录成功</title>
</head>
<body>
<%=request.getAttribute("username") %>,登录成功!
</body>
</html>
login_error.jsp的代码
<%@ page language="java" contentType="text/html; charset=GB18030"
pageEncoding="GB18030"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GB18030">
<title>登录失败</title>
</head>
<body>
登录失败
</body>
</html>
6. 增加几个java类代码
a. Encoding.java
这个类中只有一个静态方法ToSimplifiedChinese,用来将ISO-8859-1的编码,转换成GB18030的编码,以避免简体汉子出现乱码。通常情况下,在一个请求的声明周期中,只需要将从jsp页面提交的相关field做一次转换即可,该field的值在该请求的声明周期中,均会保持GB18030的编码方式。
package com.pnft.servlet;
import java.io.UnsupportedEncodingException;
public class Encoding
{
public static String ToSimplifiedChinese(String str)
{
String tempStr = null;
try
{
tempStr = new String(str.getBytes("ISO-8859-1"), "GB18030");
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
return tempStr;
}
}
b. LoginActionForm.java
这个类是为了和login.jsp对应,在前面的login.jsp中,有username和password两个输入field,因此在这个类中,我们也定义两个名称完全一样的两个属性,并给出相应的setter、getter方法。这样做的目的就是,在后续的编程中,就不再需要使用如request.getParameter方法来获取页面中提交的字段的值了,取而代之的是使用这个类的getter方法。
要注意到,在setter方法中,我们使用了Encoding类中的静态方法ToSimplifiedChinese。
package com.pnft.servlet;
import org.apache.struts.action.ActionForm;
/**
* login.jsp对应的ActionForm,即LoginActionForm是用来收集login.jsp中的表单数据的
*/
public class LoginActionForm extends ActionForm
{
private static final long serialVersionUID = -2262069096843638300L;
// username和password要和login.jsp中form的username和password保持一致,这样由MyEclipse生成的
// setter和getter方法自然就符合javabean标准的要求。事实上,如果属性username改为username1也是可以的,
// 但对应的setter、getter方法的签名不能改变。如下面的情况也是可以的:
// private String username1;
// public String getUsername()
// {
// return username1;
// }
// 一般情况下,为了方便,ActionForm类中的属性名称保持和页面表单的字段名称保持一致
private String username;
private String password;
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = Encoding.ToSimplifiedChinese(username);
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = Encoding.ToSimplifiedChinese(password);
}
}
c. LoginAction.java
package com.pnft.servlet;
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.ActionForward;
import org.apache.struts.action.ActionMapping;
/**
* 用户登录的Action
*/
public class LoginAction extends Action
{
public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception
{
LoginActionForm laf = (LoginActionForm)form;
String username = laf.getUsername();
String password = laf.getPassword();
// 下面代码模拟调用了model
if("玄机逸士".equals(username) && "admin".equals(password))
{
request.setAttribute("username", username);
// 转向登录成功页面
return mapping.findForward("success");
}
else
{
// 转向登录失败页面
return mapping.findForward("error");
}
}
}
7. 修改struts_config.xml文件,使之如下:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">
<struts-config>
<form-beans>
<form-bean name="loginForm" type="com.pnft.servlet.LoginActionForm"/>
</form-beans>
<action-mappings>
<action path="/login123"
type="com.pnft.servlet.LoginAction"
name="loginForm"
scope="request">
<forward name="success" path="/login_success.jsp"/>
<forward name="error" path="/login_error.jsp"/>
</action>
</action-mappings>
</struts-config>
8. 发布并运行
至此,所有的编码和配置工作已经完成,整个项目在MyEclipse中的目录结构如下图(根据修改后的web.xml,index.jsp并没有用到):
图6
项目成功发布后,在浏览器中的测试情况如下:
图7
输入“玄机逸士”和对应的密码“admin”,然后点击登录按钮,
图8
回到登录页面,输入“admin”和对应的密码“admin”,
图9
点击登录按钮,
图10
所有页面运行的结果,正如我们所期望的。
9. 整个请求的动作过程详解
Step1: 浏览器访问http://localhost:8090/struts_login,Tomcat查找web.xml文件,找到welcome web页面,在此例中
即为login.jsp;
Step2:login.jsp返回到浏览器,用户填写用户名和密码,然后提交;
Step3:
a. login.jsp中的form action为“login123.do”,据此查询web.xml中的<servlet-mapping> section,发现匹配模式“*.do”,据此查找对应的ActionServlet,发现servlet-name为“action”;
b. 查找<servlet> section,看是否有servlet-name为“action”;
c. 发现servlet-name为“action”的servlet对应的java类为“org.apache.struts.action.ActionServlet”,这就是中央控制
servlet,即ActionServlet。这个ActionServlet将截获所有的*.do请求,然后分发请求到相应的Action,收到Action的返
回结果后,再将结果转向(forward)到相应的页面;
d. 在web.xml文件中还规定了struts-config.xml文件的位置,如果struts的配置文件名不是struts-config.xml,那么就
需要在此处进行替换;
Step4:将请求forward到ActionServlet,其全名为上面所述的org.apache.struts.action.ActionServlet类;
Step5:
a. ActionServlet分析请求信息,其中有一项工作就是通过request.getURI得到“/struts_login/login123.do”,“/struts_login/”为应用的名称,“.do”为后缀,对于一个请求来说,他们并不包含任何有用的信息,因此真正有效的信息是“login123”;
b. ActionServlet在struts_config.xml文件中的<action-mappings> section中查找action path为“/login123”的Action类;
c. 找到了与action path为“/login123”的Action类为“com.pnft.servlet.LoginAction”,对应的ActionForm的name为
“loginForm”,有效scope为“request”,scope的缺省值为“session”,由于一个session可以有多个request,
我们这个例子,为节省session的资源,将scope设定为“request”是合理的,因为request一旦完成,相关的信
息也就不需要了,如果是“session”,即便request结束了,相关的信息将依然由于session的存在而存在,尽管
它们不再有用了。与此同时,还找到了forward name如果为“success”,则forward到login_success.jsp页面,
如果为“error”,则forward到login_error.jsp页面;
d. 根据ActionForm的name为“loginForm”,继续在struts_config.xml中的<form-beans> section中查找name为
“loginForm”对应的ActionForm的java类,即“com.pnft.servlet.LoginActionForm”,至此,就将login.jsp和java类LoginActionFrom关联了起来,这个步骤是非常重要的;
Step6: ActionServlet根据从login.jsp接收到的数据,实例化LoginActionForm类;
Step7: ActionServlet把包括在Step6中实例化的LoginActionForm对象,传递给对应的Action,在此例中为LoginAction
(见Step5 – b);
Step8: 调用相关业务逻辑(即Model部分的内容, 见LoginAction.java中“// 下面代码模拟调用了model”处);
Step9: ActionServlet根据处理结果,forward相应的页面;
Step10: 相应的页面返回给浏览器,整个过程结束。
图11