一、引入struts
Model 1结构如图1所示:
Mode1 1是一个以JSP文件为中心的模式,在这种模式中JSP页面不仅负责表现逻辑,也负责控制逻辑。专业书籍上称之为逻辑耦合在页面中,这种处理方式,对一些规模很小的项目如:一个简单的留言簿,也没什么太大的坏处,实际上,人们开始接触一些对自己来说是新的东西的时候,比如,用JSP访问数据库时,往往喜欢别人能提供一个包含这一切的单个JSP页面,因为这样在一个页面上他就可以把握全局,便于理解。但是,用Model 1模式开发大型时,程序流向由一些互相能够感知的页面决定,当页面很多时要清楚地把握其流向将是很复杂的事情,当您修改一页时可能会影响相关的很多页面,大有牵一发而动全身的感觉,使得程序的修改与维护变得异常困难;还有一个问题就是程序逻辑开发与页面设计纠缠在一起,既不便于分工合作也不利于代码的重用,这样的程序其健壮性和可伸缩性都不好。
Model 2引入了"控制器"这个概念,控制器一般由Servlet来担任,客户端的请求不再直接送给一个处理业务逻辑的JSP页面,而是送给这个控制器,再由控制器根据具体的请求调用不同的事务逻辑,并将处理结果返回到合适的页面。因此,这个servlet控制器为应用程序提供了一个进行前-后端处理的中枢。一方面为输入数据的验证、身份认证、日志及实现国际化编程提供了一个合适的切入点;另一方面也提供了将业务逻辑从JSP文件剥离的可能。业务逻辑从JSP页面分离后,JSP文件蜕变成一个单纯完成显示任务的东西,这就是常说的View。而独立出来的事务逻辑变成人们常说的Model,再加上控制器Control本身,就构成了MVC模式。实践证明,MVC模式为大型程序的开发及维护提供了巨大的便利。
二、struts工作原理
来自客户的所有需要通过框架的请求,统一由ActionServlet接收(ActionServlet Struts已经为我们写好了,只要您应用没有什么特别的要求,它基本上都能满足您的要求),根据接收的请求参数和Struts配置(struts-config.XML)中ActionMapping,将请求送给合适的Action去处理,解决由谁做的问题,它们共同构成Struts的控制器。
Action则是Struts应用中真正干活的组件,它解决的是做什么的问题,它通过调用需要的业务组件(模型)来完成应用的业务,业务组件解决的是如何做的问题,并将执行的结果返回一个代表所需的描绘响应的JSP(或Action)的ActionForward对象给ActionServlet以将响应呈现给客户。
这里要特别说明一下的是:就是Action这个类,它不应该包含过多的业务逻辑,而应该只是简单地收集业务方法所需要的数据并传递给业务对象。实际上,它的主要职责是: 校验前提条件或者声明 调用需要的业务逻辑方法 检测或处理其他错误 路由控制到相关视图
三、struts运行机制 Struts的工作流程:
在web应用启动时就会加载,初始化ActionServlet,ActionServlet从struts-config.xml文件中读取配置信息,把它们存放到各种配置对象中,当ActionServlet接收到一个客户请求时,将执行如下流程.
(1)检索和用户请求匹配的ActionMapping实例,如果不存在就返回请求路径无效信息;
(2)如果ActionForm实例不存在,就创建一个ActionForm对象,把客户提交的表单数据保存到ActionForm对象中;
(3)根据配置信息决定是否需要表单验证.如果需要验证,就调用ActionForm的validate()方法;
(4)如果ActionForm的validate()方法返回null或返回一个不包含ActionMessage的ActuibErrors对象, 就表示表单验证成功;
(5)ActionServlet根据ActionMapping所包含的映射信息决定将请求转发给哪个Action,如果相应的Action实例不存在,就先创建这个实例,然后调用Action的execute()方法;
(6)Action的execute()方法返回一个ActionForward对象,ActionServlet在把客户请求转发给 ActionForward对象指向的JSP组件;
(7)ActionForward对象指向JSP组件生成动态网页,返回给客户;
特别说明:
ActionMapping类
ActionMapping的一个实例对应了struts-config.xml文件中的一个<action>标签。其比较重要的几个属性有path,type,name,scope,input,forward,unknown等。
path为其访问路径,type是action类型,name为ActionForm的name,scope是将信息设置到request或者session中。这些都比较简单。重点说一下forward和input属性。
forward属性,Action为逻辑控制器,如果我们有转向信息而没有逻辑代码时,我们一般的做法是建立一个Action,里面只写一句转向信息的代码,如果有很多这样的Action,那么操作将非常繁琐,这时可以利用forward属性来配置其转向信息,而不用建立一个action类。使用forward属性有利于体现配置文件的完整性,应尽量使用。
unknown属性,这个是一个缺省配置,当输入的url找不到时,会跳转到unknown页面,unknown属性设置是一个页面的路径。
input属性,这个属性和异常有关系,当出现错误时,我们原来是跳转到错误页面,用input可以实现同样的效果。看代码。
- <action path="/login_struts_tag"
- type="com.bjpowernode.struts.LoginAction"
- name="loginForm"
- scope="request"
- input="/login_struts_tag.jsp"
- >
- <forward name="success" path="/login_success.jsp" />
- <!--
- <forward name="error" path="/login_error.jsp"/>
- -->
- </action>
<span style="font-size:18px;color:#3333ff;"><action path="/login_struts_tag"
type="com.bjpowernode.struts.LoginAction"
name="loginForm"
scope="request"
input="/login_struts_tag.jsp"
>
<forward name="success" path="/login_success.jsp" />
<!--
<forward name="error" path="/login_error.jsp"/>
-->
</action>
</span>
在Action中代码则改变为Return mapping.getInputForward();,和原来return mapping.findForward("error");效果一样。
DispatchAction类
DispatchAction类继承自Action类,是一种特殊的Action类。在用Action的时候,有的时候会创建很多的Action类,这时候可以用DispatchAction类来将不同的Action类化成不同的方法。即用一个DispatchAction类可以完成多个方法。
在用这个类的时候,首先要在struts-config.xml文件中的action标签多写一个parameter属性,因为在界面写访问路径的时候要带有参数,才能在DispatchAction中判断调用什么方法。配置代码为
<action path="/user/user_maint"
type="com.bjpowernode.drp.web.actions.UserAction"
name="userForm"
scope="request"
parameter="command"
parameter的含义为命令的关键字是什么,即在界面代码action ="user_maint.do?command=del";这个关键字可以替换。parameter中的值不能是execute和perform。
在得到del后,在DispatchAction中写的方法名称必须和这个一样。才能调用到这个方法上。
在DispatchAction中还有一个unspecified方法,即界面不传任何参数的时候调用到这个方法上。
DispatchAction中的execute方法,如果需要覆盖,必须显示调用。
静态和动态actionform
actionform是struts框架的核心类之一,其类似于一个实体类,收集并保存表单的上每个数据。它是struts解析了struts-config.xml文件创建出actionmapping类之后,根据actionmapping类中的name属性创建的。
actionform一般为静态类,如果为静态类,那么使用方法和面向对象中的实体类相似。actionform也可以动态配置,即在struts-config.xml文件中灵活配置,实现动态功能。
配置动态actionform时,和配置静态actionform类似,首先要有一个<form-beans>的标签,具体配置如下。
<form-beans>
<form-bean name="dynaForm"type="org.apache.struts.action.DynaActionForm">
<form-property name="username"type="java.lang.String" />
<form-property name="age"type="java.lang.Integer"/>
</form-bean>
</form-beans>
和配置静态类不一样的,就是其中多了属性配置,这个很容易理解。在配置文件配置完成后,struts会把配置的这些属性放到一个map中,这个map的value是其真正的值。
在action类中取得actionform类的值,在静态的类,直接打出对象的名字即可取得配置到其中的值,在动态的actionform中,则不能直接取得,上面说过,struts会把这些值放到map中,所以可以通过map取得。具体用法如下
DynaActionForm daf = (DynaActionForm)form;
String username =(String)daf.get("username");
Integer age = (Integer)daf.get("age");
因为他们无论form,还是DynaActionForm,都是继承与actionform,所以可以进行强制转换,强转完成后则直接可以从中get取值。
在界面如何通过er表达式取得动态actionform的值:
用户:${dynaForm.map.username }
年龄:${dynaForm.map.age }
还是利用map,在动态actionform的map中取得。
动态actionform不建议使用,毕竟动态设置有利有弊,可以灵活的设置,但是如果设置的太多,容易弄乱,配置类型的时候可能把数据类型写错,在这里大致了解就好。
ActionForm之上传
采用struts上传和普通上传大同小异,界面的表单必须写enctype="multipart/form-data",如下面所示:
<form action="upload.do"method="post" enctype="multipart/form-data">
标题:<inputtype="text" name="title"><br>
文件:<inputtype="file" name="myfile"><br>
<inputtype="submit" value="提交">
</form>
ActionForm中使用FormFile来接收上传的文件,参见:UploadActionForm.java
在Action中调用FormFile取得上传文件数据,采用流输出,即完成上传,参见:UploadTestAction.java
采用<controller/>标签配置上传参数,如:<controllermaxFileSize="10M"/>
ActionForm中代码:
- public class UploadActionForm extends ActionForm {
- private String title;
- //上传的文件必须采用FormFile声明
- private FormFile myfile;
- public String getTitle() {
- return title;
- }
- public void setTitle(String title) {
- this.title = title;
- }
- public FormFile getMyfile() {
- return myfile;
- }
- public void setMyfile(FormFile myfile) {
- this.myfile = myfile;
- }
- }
public class UploadActionForm extends ActionForm {
private String title;
//上传的文件必须采用FormFile声明
private FormFile myfile;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public FormFile getMyfile() {
return myfile;
}
public void setMyfile(FormFile myfile) {
this.myfile = myfile;
}
}
Action中代码
- public class UploadTestAction extends Action {
- public ActionForward execute(ActionMapping mapping, ActionForm form,
- HttpServletRequest request, HttpServletResponse response)
- throws Exception {
- UploadActionForm uaf = (UploadActionForm)form;
- System.out.println("title=" + uaf.getTitle());
- System.out.println("fileName=" + uaf.getMyfile().getFileName());
- //采用输出流输出到d盘根目录
- FileOutputStream fos = new FileOutputStream("d:\\" + uaf.getMyfile().getFileName());
- fos.write(uaf.getMyfile().getFileData());
- fos.flush();
- fos.close();
- return mapping.findForward("success");
- }
- }
ActionForward类
actionforward类是在action的一个处理结果,action的execute处理结果返回对象为一个actionforward。这个类一般都包含转发或者重定向信息。在struts-config.xml文件中配置forward标签时,只有四个属性,name,path,redirect,classname。
actionForward的redirect属性
当redirect=false时,将保存存储在http请求和请求上下文中的所有内容,仅在同一个应用中可用。当redirect=true时,Web客户端进行一次新的http请求,请求的资源可以在同一个应用中,也可以不在,原来的请求参数不再保存,原来的请求上下文也被清除,新的http请求仅包含ActionForward的path属性里所包含的参数。即redirect=true时为重定向,redirect=false时,为转发。
ActionForward全局与局部的概念
在配置action时,如果有很多的action配置都需要转向同样的页面,那么在每个action里面都需要重新配置一遍,这样代码重复太多,所以ActionForward有全局和局部的概念。
每个action都配置一个forward时,这就是局部ActionForward,如果有很多的action都需要同一个ActionForward。那么就可以配置全局的ActionForward。配置方法为,和Action-mapping标签平级。使用<global-forward></global-forward>标签。
<global-forwards>
<forward name="login"path="/login.jsp" redirect="true"/>
</global-forwards>
注意这个一定要配置在action-mapping外,如果同时配置了局部和全局的ActionForward,那么就近原则,先找局部的,如果局部的没有找到,则在找全局变量。
将受保护的页面放到WEB-INF下
有一些受保护的页面通过url可以直接访问,我们可以把它放到WEB-INF文件夹下,这个文件夹下的文件都是受保护的。通过url不可以直接访问。
但是如果放到了这个文件夹下,在配置forward标签的时候path属性要加上相应的路径,因为forward标签默认是从WebRoot根目录下寻找。
手动转向
有的时候在用struts开发时,我们不需要ActionForward来帮我们进行转向,这时execute方法直接返回null就可以,返回null就表示不用ActionForward来进行转向。这时候可以用response.sendRediect来自己进行转向。
如果不是必须手动转向,一般建议要用struts进行转向,因为struts是配置,灵活,而且通过配置文件就知道要转向到哪里,这些都是struts的优点。
动态ActionForward
在有的时候,一个action可能要转向很多个页面,这时候需要在配置文件中进行多个配置。但是这时候我们不希望修改Action中的代码,这时候就需要动态ActionForward了。
动态ActionForward不是有专门的配置,而是在Action中利用ActionForward的setPath方法,来动态的设置转向路径,从而实现动态效果。下面是一个实例。
- <span style="font-size: 18px;">
- public ActionForward execute(ActionMapping mapping, ActionForm form,
- HttpServletRequest request, HttpServletResponse response)
- throws Exception {
- //从界面取得要转向的页面号
- int page = Integer.parseInt(request.getParameter("page"));
- ActionForward myAf = new ActionForward();
- //设置要转向的页面号,因为是从页面获取的,页面可以随意输入,即动态
- af.setPath("/page" + page + ".jsp");
- return af;
- }
- </span>