Struts作为MVC模式的典型实现,对Model、View和Controller都提供了对应的实现组件如图:
根据上图,总结Struts架构的工作原理
Model部分:
Struts的Model部分由ActionForm 和JavaBean组成,其中,ActionForm用于封装用户请求参数,所有的用户请求参数由系统自动封装成ActionForm对象;该对象被ActionServlet转发给Action;然后Action根据ActionForm里的请求参数处理用户请求;JavaBean封装了底层的业务逻辑,包括数据库访问等等,在更复杂的应用中,JavaBean所代表的绝非一个简单的JavaBean,可能是EJB组件或者其他的业务逻辑组件;
View部分:
Struts的View部分采用JSP实现,Struts提供了丰富的标签库,通过这些标签库可以最大限度地减少脚本的使用,这些自定义的标签库可以实现与Model的有效交互,并增加了显示功能;
整个应用由客户端请求驱动,当客户端请求被ActionServlet拦截时,ActionServlet根据请求决定是否需要调用Model处理用户请求,当用户请求处理完成后,其处理结果通过JSP呈现给用户;
Controller部分
Struts的Controller由两部分组成,一是系统核心控制器,另一个是业务逻辑控制器。
系统核心控制器对应上图的ActionServlet,该控制器由Struts框架提供,继承HttpServlet类,因此可以配置一个标准的Servlet。该控制器负责拦截所有HTTP请求,然后根据用户请求决定是否需要调用业务逻辑控制器,如果需要调用业务逻辑控制器,则将请求转发给Action处理,否则直接转向请求的JSP页面;
业务逻辑控制器负责处理用户请求,但其本身不具有处理功能,而是调用Model来完成处理,他对应上图的Action部分;
Struts1的流程:
Struts框架中所使用的组件:
1、ActionServlet: 核心控制器(工作构建数据结构流程)
2、Action.Class 包含事务逻辑
3、ActionForm :ActionForm:封装用户请求的参数 (显示模块数据)
4、ActionMapping: 用于封装请求处理流程的关系(帮助控制器将请求映射到操作)
5、ActionForward :封装响应(用来指示操作转移的对象)
6、ActionError: 用来存储和回收错误
7、Struts标记库: 可以减轻开发显示层次的工作
Struts1的开发步骤:
1、将Struts1下的包添加到lib下
2、在web.xml中配置核心控制器
3、生成Struts的配置文件
4、生成相应的action、model以及jsp
下面我们先来以Struts1.2+JDBC CRUD Demo为例
整体目录结构为:
一、web.xml:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.5" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <servlet> <servlet-name>actionServlet</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>debug</param-name> <param-value>3</param-value> </init-param> <init-param> <param-name>detail</param-name> <param-value>3</param-value> </init-param> <load-on-startup>0</load-on-startup> </servlet> <servlet-mapping> <servlet-name>actionServlet</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <!-- 过滤器 --> <filter> <filter-name>encoding</filter-name> <filter-class>com.iflytek.filter.EncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
说明:
从web.xml中,所有以.do结尾的请求都将有ActionServelt拦截,同时该Servlet还有加载Struts配置文件的责任,ActionServelt默认加载web-inf路径下,或者改变了文件名,则应采用下方式配置:
<servlet> <servlet-name>actionServelt</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/myConfig.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>actionServelt</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
这样指定了ActionServlet的配置文件myConfig.xml文件,最为init-param参数载入,载入时指定了参数名config,为固定值,config是Struts固定的参数名,struts负责解析该参数,并加载该参数指定的配置文件。当有多个Struts配置文件时,只需添加init-param元素即可
<init-param> <param-name>config</param-name> <param-value>/WEB-INF/myConfig1.xml</param-value> </init-param> <init-param> <param-name>config/first</param-name> <param-value>/WEB-INF/myConfig2.xml</param-value> </init-param>
二、ActionFrom
package com.iflytek.struts.form;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.upload.FormFile;
import com.iflytek.vo.User;
/**
* @author xudongwang 2011-10-20
*
*/
@SuppressWarnings("serial")
public class UserForm extends ActionForm {
private int cp = 1;
private int ls = 5;
// user.do?status=list
private String status;
// 这里使用对象接受,这里必须new出来,否则设不上值
private User user = new User();
// 当使用struts需要进行文件上传操作时,需要在Struts的ActionForm通过一个类型为FormFile的对象来接受文件的内容
private FormFile img;
/**
* Method validate
*
* @param mapping
* @param request
* @return ActionErrors
*/
public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request) {
// 注意需要判断状态,并不是所有情况都需要验证
ActionErrors errors = new ActionErrors();
if ("insert".equals(status)) {
if (this.user.getAccount() == null
|| this.user.getAccount().trim().equals("")) {
errors.add("accout", new ActionMessage("account.null"));
}
if (this.img == null || this.img.getFileSize() == 0) {
errors.add("img", new ActionMessage("img.null"));
} else if (this.img.getFileSize() > 1024000) {
errors.add("img", new ActionMessage("img.size"));
} else {
this.user.setPostDate(new Date());
String fileName = new Date().getTime() + "";
// 文件扩展名,从最后一个点开始截取字符串
String kz = this.img.getFileName().substring(
this.img.getFileName().lastIndexOf("."));
this.user.setPhoto(fileName + kz);
}
}
return errors;
}
/**
* Method reset
*
* @param mapping
* @param request
*/
public void reset(ActionMapping mapping, HttpServletRequest request) {
}
public int getCp() {
return cp;
}
public void setCp(int cp) {
this.cp = cp;
}
public int getLs() {
return ls;
}
public void setLs(int ls) {
this.ls = ls;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public FormFile getImg() {
return img;
}
public void setImg(FormFile img) {
this.img = img;
}
}
说明:
配置ActionForm,需要包含ActionForm类才可以,Struts1要求ActionForm必须继承Struts的基类org.apache.struts.action.ActionForm。ActionForm的实现非常简单,该类只是一个普通的JavaBean,只要为每个属性提供对应的getter和setter方法即可。
ActionForm用于封装用户的请求参数,而请求参数是通过JSP页面的表单域传递过来的,所以要保证ActionForm的参数属性的getter和setter后缀名字与表单域的名字相同,而与属性名无关,可以随意取(即客户端请求中有个属性名为name,则在JavaBean中需要getName和setName,而该JavaBean属性名可以任意,因为属性为private的,所以是通过构造器取的);
三、Action
package com.iflytek.struts.action;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.actions.DispatchAction;
import com.iflytek.factory.DAOFactory;
import com.iflytek.struts.form.UserForm;
import com.iflytek.vo.User;
/**
* DispatchAction:分发Action用来解决在Action中需要判断当前操作状态的问题,
* 使用分发Action后可以根据传递状态参数自动调用对应的方法
*
* @author xudongwang 2011-10-20
*
*/
public class UserAction extends DispatchAction {
/**
* Method list 方法名list根据页面传入的status=list
*
* @param mapping
* @param form
* @param request
* @param response
* @return ActionForward
*/
public ActionForward list(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
UserForm userForm = (UserForm) form;
List<User> users = null;
int allRecorders = 0;
try {
users = DAOFactory.getUserDAOInstance().findAll(userForm.getCp(),
userForm.getLs());
allRecorders = DAOFactory.getUserDAOInstance().getAllCount();
} catch (Exception e) {
e.printStackTrace();
}
request.setAttribute("cp", userForm.getCp());
request.setAttribute("ls", userForm.getLs());
request.setAttribute("users", users);
request.setAttribute("allRecorders", allRecorders);
return mapping.findForward("userlist");
}
public ActionForward insert(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
UserForm userForm = (UserForm) form;
boolean flag = false;
try {
flag = DAOFactory.getUserDAOInstance().doCreate(userForm.getUser());
} catch (Exception e) {
e.printStackTrace();
}
if (flag) {
// 开始保存文件
// 使用字节流FileOutputStream来输出上传的文件
try {
FileOutputStream os = new FileOutputStream(new File(this
.getServlet().getServletContext().getRealPath("/")
+ "upload/" + userForm.getUser().getPhoto()));
os.write(userForm.getImg().getFileData());
os.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
request.setAttribute("message", flag ? "添加成功!" : "添加失败!");
request.setAttribute("path", "user.do?status=list");
//return mapping.findForward("forward");
//这里不使用struts进行页面的跳转
ActionForward forward = new ActionForward();
forward.setPath("/forward.jsp");
return forward; }
}
说明:
Action负责管理与之关联的ActionForm,它不仅需要配置实现类,还需要配置Aciton的path属性,用于被用户请求。
1、Action需要继承Aciton,并且重写execute方法
2、通过ActionForm,可使Action无须从HTTP请求中解析参数,而直接从ActionForm中取
Action的实现:
Action从转发过来的ActionForm中解析请求参数,对应的ActionForm则由ActionServlet在接受到用户请求时,负责实例化;
实际过程是,ActionServlet拦截到用户请求后,根据用户的请求,在配置文件中查找对应的Action,Action的 name属性指定了用于封装请求参数的ActionForm,然后ActionServlet将创建默认的ActionForm实例,并调用对应的 setter方法完成ActionForm的初始化;
在上面的过程中,ActionServlet采用反射机制来完成对象的创建以及初始化等过程;
四、struts-config.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN" "http://struts.apache.org/dtds/struts-config_1_3.dtd"> <!-- 业务逻辑控制器,处理用户请求,但其本身不具有处理功能,而是调用Model来完成处理 --> <struts-config> <!-- 以下配置是针对ActionForm,form-beans中包含多个form-bean --> <form-beans> <form-bean name="userForm" type="com.iflytek.struts.form.UserForm" /> </form-beans> <!-- 公共异常的处理,开发中使用不多 --> <global-exceptions> <exception key="num" type="java.lang.NumberFormatException" path="excep"></exception> </global-exceptions> <!-- 公共的跳转路径 --> <global-forwards> <forward name="excep" path="error.jsp"></forward> </global-forwards> <!-- 以下配置是针对于Action, action-mappings用于封装请求处理流程的关系(帮助控制器将请求映射到操作)可以包含多个Action --> <action-mappings> <action attribute="userForm" input="/error.jsp" name="userForm" parameter="status" path="/user" scope="request" type="com.iflytek.struts.action.UserAction" cancellable="true" unknown="true"> <forward name="userlist" path="/UserList.jsp"></forward> <forward name="forward" path="/forward.jsp"></forward> </action> </action-mappings> <!-- 资源文件 所在的路径,资源文件的后缀名必须为properties--> <message-resources parameter="com.iflytek.struts.Resources" /> </struts-config>说明:
1、parameter名表示页面传参的名称status=list;
2、一个action只能对应一个actionform,而一个actionform可以有多个action使用;
3、input用来作错误页的,当validate方法执行后有错误,则自动跳转到该页;
4、所有struts-config.xml中配置的路径必须先加/,表示路径从webroot下开始定义 ;
5、path表示该action的虚拟路径,必须加/,而不需要加.do的后缀 ;
6、scope表示Action的属性方法,request表示每次请求都重新建立新的action ;
7、type表示Action的包.类名;
8、在action中可以包含多个不同的forward路径,而Forward分局部和全局两种,前者是在Action里配置,仅对该Action有效,后者单独配置,对所有的Action都有效,当每个Action在转发时,首先在局部Forward中查找与之对应的Forward,如果没找到,则才会到全局中查找,所以局部Forward可以覆盖全局Forward;
9、forward中的name是forward的唯一标识,在action代码中执行跳转时需要通过该name来查找对应的路径,path表示真正跳转的路径;
10、redirect:设置是否使用重定向,只能是true和false两个值;
11、unknown="true"表示如果没有xx.do则会跳转到错误页面
五、JSP
insert.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>添加人员</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> </head> <body> <center> <form action="user.do" method="post" enctype="multipart/form-data"> 用户名: <input type="text" name="user.account" /> <br /> 密码: <input type="text" name="user.password" /> <br /> 性别: <input type="radio" name="user.sex" value="男" checked="checked" /> 男 <input type="radio" name="user.sex" value="女" /> 女 <br /> 真实姓名: <input type="text" name="user.realName" /> <br /> 照片: <input type="file" name="img" /> <br /> <input type="hidden" name="status" value="insert"> <input type="submit" value="添加"> </form> </center> </body> </html>
UserList.jsp
<%@ page language="java" pageEncoding="UTF-8"%> <%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%> <%@ taglib uri="http://struts.apache.org/tags-html" prefix="html"%> <%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic"%> <%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html:html lang="true"> <head> <title>用户列表</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> </head> <body> <center> <a href="insert.jsp">添加人员</a><br/> <table border="1" width="60%"> <tr> <td> 用户名 </td> <td> 性别 </td> <td> 注册日期 </td> <td> 照片 </td> <td> 操作 </td> </tr> <logic:present name="users" scope="request"> <logic:iterate id="user" name="users" scope="request"> <tr> <td> ${user.account } </td> <td> ${user.sex } </td> <td> <bean:write name="user" property="postDate" format="yyyy-MM-dd" /> </td> <td> <img alt="${user.account }" src="upload/${user.photo }" width="80px" height="60"> </td> <td> <a href="#">修改</a> <a href="#">删除</a> </td> </tr> </logic:iterate> </logic:present> </table> <jsp:include page="split_page.jsp"> <jsp:param name="currentPage" value="${cp}" /> <jsp:param name="lineSize" value="${ls}" /> <jsp:param name="allRecorders" value="${allRecorders}" /> <jsp:param name="searchFlag" value="FALSE" /> <jsp:param name="lineSizeFlag" value="TRUE" /> </jsp:include> </center> </body> </html:html>