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>