03 00Struts 1.x开发框架

1 Struts 1.x简介

Struts是由Apache在2002年推广的一个开源项目,主要是进行MVC实现的开发框架。但是为什么需要为MVC设计一个框架呢?为了更好的解释下面做个对比:
(1)考虑一:如果使用标准的开发技术(JSP+Servlet+JavaBean),如果没有进行任何的设计会发现代码重复量惊人。
|——验证操作需要重复编写;
|——用户需要自己手工接收参数,手工进行数据的转型,而后设置到VO对象里面;
|——所有的跳转路径都统一定义在了程序之中,后期维护困难;
|——文件上传操作需要自己手工处理。
(2)考虑二:应该进行更加合理的设计(开发了一个DispatcherServlet),它提供有如下好处:
|——自动进行数据的接收以及VO类对象的转换,将VO类的概念进一步提升;
|——可以定义专门的资源文件进行各种信息以及跳转路径的保存;
|——上传的操作对用户而言几乎隐藏,用户接收参数的时候也不再需要区分到底是否有封装还是没有封装;
|——提供统一的验证操作形式,保证服务器端的验证简化;
|——利用反射机制的操作进行一个控制器的多业务处理。
利用了自己的设计来解决开发的重复性操作固然是一件非常兴奋的事情,但是事实上这样的开发操作如果用于直接的项目设计之中,那么要想实现一个几乎完美的DispatcherServlet实在是太难了。而且如果你真的开发完成了,并且希望在你的项目中实际使用,那么至少应该将代码变为接近于90%
的完美无缺才可以。

所以在后来使用JSP、Servlet进行MVC标准开发的过程之中,就特别需要有一家组织可以帮助所有的公司提供这样一个近乎完美的开发的一组类库,那么就可以为我们的开发与设计节约大量的时间,在这样的背景下产生了最早的Struts技术。随后随着版本的提升,Struts又产生了1.1、1.2、1.3(并且永恒定格在了1.3版本)。

在Struts 1.x中“完美”的实现MVC设计模式的体现,在这里面用户可以使用统一的风格进行数据的验证,用户也可以使用第三方验证框架(极其不好用,没有任何一个开发框架的验证框架好用)、支持FileUpload进行上传并且支持多业务处理等等优势,所以在最初的时候Struts 1.x被大量的采用。

被Struts 2.x所替代主要是因为Struts 1.x有一些天然的劣势(初期的设计问题),而在Struts 2.x支持了拦截器的操作,到了今天所有的新的项目已经很难见到Struts 1.x身影。

在最初的几年里面,只要你从事Java开发,如果不会Struts那么基本上就不叫Java开发。

面试题:请解释MVC与Struts的区别?
答:MVC是一个标准的设计模式,重点在于控制层、显示层、模型层彼此独立。而Struts只是MVC的一种实现,如果把MVC当做接口来看,那么Struts就是这个接口的实现子类。

Struts 1.x也就出现在维护项目的可能性居多,而在新项目开发之中最次也要使用Struts 2.x。

2 开发第一个Struts 1.x程序(开发配置)

下面将实现一个最简单的用户登录程序,本次的操作主要是在表单中输入用户名和密码西南西,而后要求判断用户名和密码是否有内容(服务器端验证),随后验证的信息是“lks/19990821”,如果成功则显示欢迎信息,否则显示失败的信息。

1、MyEclipse已经默认支持了Struts 1.x和Struts 2.x的开发,那么下面按照步骤进行:
(1)建立一个新的项目:Struts1Project;
(2)在Struts 1.x的时代里面还没有所谓的Annotation配置支持,所以此处如果要想使用Struts 1.x建议生成web.xml。
2、所谓的开发框架就是一组开发程序的jar包,那么在MyEclipse里面可以直接为项目配置这些jar包。
(1)选择项目,而后点右键,选择MyEclipse的项目支持。
3、进入Struts 1.x配置页面;
(1)本次使用1.3版本。

随后的界面里面需要定义一些配置信息,这些配置信息包含有如下内容:
|——Servlet的处理路径:Struts 1.x中的Servlet路径是一个经典的*.do
|——随后要求输入项目的程序包org.lks;
|————这个时候会发现自动创建有一个文件“ApplicationResources”,保存文本资源;

|——随后会询问所需要的Struts开发包。

项目建立完成之后会自动在项目中出现以下内容:
(1)会自动的将Struts 1.x的开发包配置到CLASSPATH之中;
(2)在WEB-INF目录下会自动创建有一个struts-config.xml文件,这个是Struts的核心配置文件;
(3)在src目录下(最终会输出到WEB-INF/classes下)存在有一个ApplicationResource.properties;

3 开发第一个Struts 1.x程序(程序编写)

1、建立一个login.jsp页面:
(1)在建立这个页面的时候稍微有一些不同,将使用Struts 1.x提供的标签完成。
范例:程序代码


此时发现在定义表单的时候所使用的形式不太一样了,因为它使用了标签处理(为了方便回填):
|——<form><html:form action="" method="post" focus="login">
|——<input type="text"><html:text property="username" />
|——<input type="password"><html:password property="password" />
|——<input type="submit"><html:password property="password" />
|——<input type="reset"><html:reset value="reset"/>
在输入组件中定义的properties实际上在最后页面运行的时候会自动生成id与name两个属性,如果定义了以下内容:<html:text property="username",那么就会生成的<input type="text" name="username">,除此之外,还可以生成与程序中的的接收属性进行对应。

2、配置错误信息,修改ApplicationResource.properties文件

# 这些验证的错误信息是给ActionForm准备的
username.input.null=<li>登录用户名不允许为空!</li>
password.input.null=<li>登录密码不允许为空!</li>
# 这些登录结果信息是给Action准备的
login.success=用户登陆成功,欢迎光临!
login.failure=用户登陆失败,请重新登录!

3、编写处理程序,但是Struts 1.x的处理程序是分为两种类型:Action(负责跳转,与Servlet功能相似)、ActionForm(进行数据验证使用的)。

随后会首先进入到创建ActionForm操作对话框,在这里面输入以下的内容:
(1)Use case:表示本次操作的模块名称,输入“mylogin”;
(2)随后会自动出现一个name的数据“myloginForm”,它是在struts-config.xml文件里面出现的;
(3)让这个ActionForm继承org.apache.struts.action.ActionForm类;
(4)设置属性,在JSP里面写了两个属性:usernamepassword

随后下一步进入到Action的配置。
(1)首先在这里面最需要配置的是一个访问路径,也就是表单提交路径/mylogin
(2)此Action类继承org.apache.struts.action.Action父类;
(3)随后设置input Source

4、首先在MyloginActionForm中编写如下代码:
(1)首先可以发现建立的属性都会自动生成setter、getter对应的方法;
(2)数据验证的代码操作要求写在validate方法里面;

package org.lks.struts.form;

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;

@SuppressWarnings("serial")
public class MyloginForm extends ActionForm {
	private String password;

	private String username;

	/** 
	 * 此方法主要进行用户输入数据的验证,如果现在输入的数据有错误,则将错误信息保存在ActionMessage类对象里面
	 * 并且将所有的ActionMessage类对象可以统一保存在ActionErrors,如果在返回的时候返回的是null或者ActionErrors返回的内容没有包含任何的错误信息
	 * 那么会将请求继续交给Action进行处理,否则会返回到一个错误路径(input Source)。
	 * @param mapping 表示所有映射信息,此类一般不使用
	 * @param request 可以接收用户的请求参数
	 * @return ActionErrors 如果返回null或者里面保存的错误信息为0,那么表示正确
	 */
	public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
		
		// TODO Auto-generated method stub
		ActionErrors errors = new ActionErrors();  //保存错误信息
		//当用户发送请求后所有的输入的参数都会自动调用setter赋值,当执行到此方法时,一定是已经处理完参数了
		if( this.username == null || "".equals(this.username)){  //输入的username参数内容为空
			//add()方法有两个参数,第一个参数表示的是提交参数的名字
			//第二个参数表示读取的错误的key,错误的信息利用ActionMessage读取
			errors.add("username", new ActionMessage("username.input.null"));
		}
		
		if( this.password == null || "".equals(this.password)){ //输入的password参数内容为空
			errors.add("password", new ActionMessage("password.input.null"));
		}
		
		return errors;
	}

	public void reset(ActionMapping mapping, HttpServletRequest request) {
		// TODO Auto-generated method stub
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}
}

5、开发Action程序,一旦代码执行到Action之后,那么就表示数据验证通过,一定是符合要求的数据。
(1)在struts-config.xml中配置跳转的路径,现在假设成功或者失败都跳转到login.jsp页面:

<action attribute="myloginForm" input="/login.jsp" name="myloginForm"
	path="/mylogin" scope="request" type="org.lks.struts.action.MyloginAction"
	cancellable="true">
	<forward name="success" path="login.jsp"></forward>
	<forward name="failure" path="login.jsp"></forward>
</action>

(2)随后编写MyloginAction程序类:

package org.lks.struts.action;

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;
import org.lks.struts.form.MyloginForm;

public class MyloginAction extends Action {

	/** 
	 * 此操作主要是进行用户的请求处理,类似于doGet()或者是doPost()方法功能
	 * @param mapping 此类主要负责读取struts-config.xml文件配置
	 * @param form 表示要操作的ActionForm,使用它的目的是为了能够接收参数
	 * @param request 用户的请求
	 * @param response 用户回应
	 * @return ActionForward 表示返回路径信息,通过配置文件取出映射的key
	 */
	public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request,
			HttpServletResponse response) {
		MyloginForm myloginForm = (MyloginForm) form;// TODO Auto-generated method stub
		System.out.println();
		if ("lks".equals(myloginForm.getUsername()) && "19990821".equals(myloginForm.getPassword())) {
			request.setAttribute("msg", super.getResources(request).getMessage("login.success"));
			return mapping.findForward("success");
		}else{
			request.setAttribute("msg", super.getResources(request).getMessage("login.failure"));
			return mapping.findForward("failure");
		}
	}
}

6、此时既然有可能出现错误信息,那么在login.jsp页面里面使用一个标签输出:

<html:errors/>
${msg}

此时一个最为基础的Struts 1.x的应用就实现了,但是可能最大的感受在于,代码编写很受限制,开发的结构很规矩。

4 Struts 1.x原理分析

Struts 1.x是MVC的最基础实现,那么在整个Struts 1.x的开发过程之中,请求的处理流程是最需要我们关注的,同时也是我们可以发现问题的所在。

整个Struts之中是将一个Servlet的控制变为了两个部分:ActionFrom、Action。它这样拆分的目的是为了让验证与具体的控制相分离。分离的目的是为了让验证与核心操作分开,但是同时也会产生一个新的问题:如果在项目之中,每一个Action都要进行一个业务处理的话,那么这每一个Action都需要使用一个ActionForm进行匹配,结果就是ActionForm会无限多。Struts 1.x最大的缺点在于ActionForm过多。Struts 2.x以及新的各种开发框架之中最大的有点在于利用拦截器的方式来解决ActionForm的问题。

1、只要是MVC的操作,那么其基本的核心流程:JSP提交给Servlet,而后由Servlet再跳转到指定的JSP页面;

2、在使用Struts框架的时候由于Struts框架里面支持有各种标签操作,所以在定义JSP的时候可以使用HTML标签生成HTML元素,那么这种标签的好处是可以帮助用户隐藏一些代码的细节,例如:<html:text property="username">,那么这样的操作本身会生成HTML元素,但是同时它也可以自动的与ActionForm中的属性相关联(如果不使用这个标签,参数名称必须在ActionForm中定义好);
|——同时在使用<html:form>标签的时候由于其会设置一个Action的处理路径(action="mylogin.do"),所以在生成HTML代码之前都会默认先调用一个ActionForm的reset()方法表示进行内容的初始化;

3、随后表单要进行提交的时候输入的是struts-config.xml文件里面配置的action的路径,同时后面要加上*.do,因为在Struts里面所有的的处理提交都是先提交给一个ActionServlet的,那么这个的配置在web.xml文件里面定义;

<servlet>
    <servlet-name>action</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>
    <load-on-startup>0</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>

之所以项目中的struts-config.xml文件有效,那么是因为在ActionServlet之中要读取此路径中的内容,读取后一定要进行SAX解析进行文件的分析。这个ActionServlet是在项目加载的时候默认先执行的,也就是说在项目容器一旦运行后那么会立刻加载struts的配置文件。而这个ActionServlet也可以作为请求的处理,处理的时候只需要后缀为.do就可以找到这个处理的Servlet。

4、当ActionServlet接收到用户的请求之后,会自动的将请求处理的路径与指定的ActionForm关联,而后利用反射动态设置ActionForm类对象中的属性,调用setter方法,提交的路径是在Action上配置的,而Action与ActionForm的关联是在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">

<struts-config>
  <form-beans >   //在此处配置所有的ActionForm类
    <form-bean    //表示一个ActionForm的配置
    	name="myloginForm"   //此ActionForm的名字,此名称在本文件有效
    	type="org.lks.struts.form.MyloginForm" />  //ActionForm对应的程序类名称

  </form-beans>

  <global-exceptions />  //定义全局异常
  <global-forwards />  //定义全局条件,例如:errors.jsp、forward.jsp
  <action-mappings >  //定义所有的Action,ActionForm与Action的关联就在于此
    <action   //配置一个Action
    	attribute="myloginForm"   //本Action要使用的ActionForm
    	input="/login.jsp"   //默认的错误处理页
    	name="myloginForm"   //本Action要使用的ActionForm
    	path="/mylogin"   //此Action的操作路径,与*.do结合使用
    	scope="request"   //可选的范围有两种:request、session
    	type="org.lks.struts.action.MyloginAction"  //对应的Action类名称
    	cancellable="true"> 
	<forward   //局部跳转,只为一个Action服务
		name="success"   //跳转名称(key),程序读取key取得跳转路径
		path="/login.jsp">  //跳转的路径
	</forward>
	<forward name="failure" path="/login.jsp"></forward>
</action>

  </action-mappings>

  <message-resources   //配置的是全局资源,可以实现国际化操作
  	parameter="org.lks.struts.ApplicationResources" />  //定义资源文件的路径
</struts-config>


5、当ActionForm里面有内容之后(都是利用反射设置的),那么就会自动调用validate方法进行数据的验证,如果出现了错误,则向ActionErrors里面进行错误信息的保存,每一个错误信息都使用ActionMessge保存,ActionMessage的构造方法里面需要设置错误对应的资源文件中定义的key的信息。

username.input.null=xxx
password.input.null=xxx
//读取操作
errors.add("参数名称", new ActionMessage("username.input.null"));

如果此时validate()方法返回的内容为null(不验证)或者说返回的ActionErrors(相当于一个集合)中的错误的数据个数为0,表示验证通过,则继续将处理交给Action完成,但是如果出现了错误(验证不通过),则会使用input属性定义跳转路径,将请求跳转回指定的页面并且使用<html:errors>标签进行显示。

6、当验证通过之后会将请求交给Action进行处理,而在Action里面(必须继承一个指定的父类),会默认执行execute()方法进行控制器的执行,在控制器里面正常应该调用业务层的方法,而后根据结果跳转到指定的路径。

所有的跳转路径都在struts-config.xml文件里面进行了定义:

<action attribute="myloginForm" input="/login.jsp" name="myloginForm"
		path="/mylogin" scope="request" type="org.lks.struts.action.MyloginAction"
		cancellable="true">
		<forward name="success" path="/login.jsp">
		</forward>
		<forward name="failure" path="/login.jsp"></forward>
	</action>

通过ActionMapping类可以读取struts-config.xml文件中的配置,并且利用这个类对象的findForward()方法可以设置要读取数据的key的信息,而后要跳转的路径会使用ActionForward类对象进行包装。而后在ActionServlet里面根据接收到的ActionForward里面保存的路径key(name=“success”)取出对应的value(path="/login.jsp"),而后利用RequestDispatcher接口实现跳转操作。

总结:Struts 1.x的缺点
(1)随着项目开发的进行,在Struts开发里面一定会存在有多个Action,但是每一个Action都必须有一个与之对应的ActionForm绑定,那么开发的代码量实在是太大了,而且有些操作是不需要验证的,但是考虑到结构的完整性,依然需要编写ActionForm,太重复;
(2)ActionForm是需要编写验证操作,但是如果真的分开来写,那么会出现一个问题,几乎99%的验证代码都是重复的;
(3)所有的要使用的Action、ActionForm都要求在struts-config.xml文件里面进行定义,那么如果代码开发量一大,这个文件的庞大程度几乎就不可想象,而且文件一大,在进行一些代码修改的时候可能就会出现错乱。

综合来讲,整个Struts的开发的确是满足了标准的MVC处理结构,,但是最终发现,这种实现的方式也存在有一定的历史局限性(在2005年之前所有的开发都是基于配置文件编写的),以及初期的设计失误(ActionForm操作上很失败),所以Struts 1.x虽然是最早的一个成熟的MVC开发框架,但是其却因为很多原因不能够得到长足的发展。

总结:Struts1.x的优点
(1)开发结构严谨,所有的开发者必须按照指定的结构继承类并且覆写指定的方法;
(2)提供了大量的标签:
|————html标签:在于可以进行自动的数据填充以及数据回显上;
|————logic标签:实现页面的逻辑处理(循环、判断),有了JSTL之后这个也没特点;
|————bean标签:在没有EL之前,可以使用bean标签输出对象的属性内容,有EL之后这个操作不用了。
(3)可以直接进行VO对象的转换以及分发操作。

Struts是工作在显示层与控制层下的框架,它不涉及业务层与数据层。

5 程序功能加强

此时的项目里面所有的数据都是分开传递的,但是在实际的开发之中,所有的数据最好都是利用VO类进行传递,所以在Struts 1.x里面支持参数与VO类的转换。
范例:定义一个Member.java的VO类

@SuppressWarnings("serial")
public class Member implements Serializable {

	private String username;
	private String password;
	
	
	public Member() {}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
	
	
}

随后为了可以接收,在ActionForm里面定义VO类的对象。
范例:修改ActionForm,此时使用VO接收数据

@SuppressWarnings("serial")
public class MyloginForm extends ActionForm {
	private Member member = new Member();
	private String code;

	public void setCode(String code) {
		this.code = code;
	}

	public Member getMember() {
		return member;
	}

	public void setMember(Member member) {
		this.member = member;
	}

	/** 
	 * 此方法主要进行用户输入数据的验证,如果现在输入的数据有错误,则将错误信息保存在ActionMessage类对象里面
	 * 并且将所有的ActionMessage类对象可以统一保存在ActionErrors,如果在返回的时候返回的是null或者ActionErrors返回的内容没有包含任何的错误信息
	 * 那么会将请求继续交给Action进行处理,否则会返回到一个错误路径(input Source)。
	 * @param mapping 表示所有映射信息,此类一般不使用
	 * @param request 可以接收用户的请求参数
	 * @return ActionErrors 如果返回null或者里面保存的错误信息为0,那么表示正确
	 */
	public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
		
		// TODO Auto-generated method stub
		ActionErrors errors = new ActionErrors();  //保存错误信息
		//当用户发送请求后所有的输入的参数都会自动调用setter赋值,当执行到此方法时,一定是已经处理完参数了
		if( this.member.getUsername() == null || "".equals(this.member.getUsername())){  //输入的username参数内容为空
			//add()方法有两个参数,第一个参数表示的是提交参数的名字
			//第二个参数表示读取的错误的key,错误的信息利用ActionMessage读取
			errors.add("username", new ActionMessage("username.input.null"));
		}
		
		if( this.member.getPassword() == null || "".equals(this.member.getPassword())){ //输入的password参数内容为空
			errors.add("password", new ActionMessage("password.input.null"));
		}
		
		return errors;
	}

	public void reset(ActionMapping mapping, HttpServletRequest request) {
		// TODO Auto-generated method stub
	}
}

随后考虑到登录的安全性问题,使用验证码完成。
范例:在ActionForm里面需要定义验证码的接收以及检查

code.input.null=<li>验证码不允许为空!</li>
code.input.error=<li>验证码错误!</li>

if( this.code == null || "".equals(this.code) ){
			errors.add("code", new ActionMessage("code.input.null"));
		}else{ //如果已经输入了验证码,那么需要判断其有效性
			String rand = (String) request.getSession().getAttribute("rand");
			if(!rand.equalsIgnoreCase(this.code)){
				errors.add("code", new ActionMessage("code.input.error"));
			}
		}

那么随后继续进行修改,判断一下,如果输入的用户名为admin,那么直接告诉用户此为非法信息,不允许登录检查,直接返回错误信息。
范例:修改Action操作

noauther.username.error=<li>非法的用户名,请检查后重新输入!</li>

if("admin".equalsIgnoreCase(myloginForm.getMember().getUsername())){
			//所有错误信息在之前的ActionForm中都保存在了ActionErrors中
			//但是在新的Struts 1.x版本里面提供了一个子类ActionMessages
			ActionMessages msg = new ActionMessages();
			msg.add("username", new ActionMessage("noauthor.username.error"));
			super.addErrors(request, msg);  //保存信息
			return mapping.getInputForward();
		}

范例:随后前台页面中重新定义属性名称以及验证码的导入

<body>
  	<html:errors/>
  	${msg }
    <html:form action="mylogin.do" method="post" focus="login">
      <table border="0">
        <tr>
          <td>Username:</td>
          <td><html:text property="member.username" /></td>
        </tr>
        <tr> 
          <td>Password:</td>
          <td><html:password property="member.password" /></td>
        </tr>
        <tr>
        	<td>Code:</td>
        	<td><html:text property="code" maxlength="4" size="4"/></td>
        </tr>
        <tr>
          <td colspan="2" align="center">
          	<html:submit value="submit"/>
          	<html:reset value="reset"/>
          </td>
        </tr>
      </table>
    </html:form>
  </body>

此时的程序已经实现了更加丰富的检查操作,那么通过这个程序实际上可以发现以下的特点:
(1)ActionForm的确是完成所有的验证开发操作,所有的验证都可以由ActionForm进行检查;
(2)Action也可以保存错误信息,并且跳转到错误的显示页上。

大部分情况下所有的错误的检查不应该由Action进行,Action大部分只进行业务调用后的跳转。

6 多业务处理

在实际的所有开发之中,一个Action肯定完成的是一组模块,如果说假设每一个Action只完成一个功能,那么如果有部门表的增加、修改、删除、更新操作,那么意味着你就需要编写四个操作,那么同时又要对应四个ActionForm,这一点是绝对不可能出现在代码开发之中,所以在Struts里面提供有专门的分发处理操作功能,但是这个功能是利用参数的内容来实现的。

下面编写一个简单的多业务处理,假设有一个部门数据要实现CRUD。
范例:定义一个department.java的VO类

@SuppressWarnings("serial")
public class Department implements Serializable{
	private Long did;
	private String dname;
	
	public Department(){}

	public Long getDid() {
		return did;
	}

	public void setDid(Long did) {
		this.did = did;
	}

	public String getDname() {
		return dname;
	}

	public void setDname(String dname) {
		this.dname = dname;
	}
}

随后一定要建立Action与ActionForm,但是这组Action与ActionForm应该是为Department类服务的,所以建立的名称就以“Department”为主。

如果要进行分发处理,必须让Action继承org.apache.struts.actions.DispatchAction父类。随后需要设置一个分发的处理参数,参数名称设置为status

随后观察一下struts-config.xml文件中的配置:

<action attribute="departmentForm" input="/error.jsp" name="departmentForm"
			parameter="status" path="/department" scope="request"
			type="org.lks.struts.action.DepartmentAction" cancellable="true" />

以后如果要想正确执行多业务的的处理方法,必须传递一个status参数,如果status=insert,那么表示调用Action中的insert()方法。

那么此时的Action类里面不允许定义有execute()方法名称,一旦定义它,所有的方法的分发调用将无效。
范例:定义DepartmentAction

package org.lks.struts.action;

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 org.lks.struts.form.DepartmentForm;

public class DepartmentAction extends DispatchAction {
	
	public ActionForward insert(ActionMapping mapping, ActionForm form, HttpServletRequest request,
			HttpServletResponse response) {
		DepartmentForm departmentForm = (DepartmentForm) form;
		System.out.println("*** insert: " + "id: " + departmentForm.getDepartment().getDid() + ", name: " + departmentForm.getDepartment().getDname());
		return null;
	}
	
	public ActionForward delete(ActionMapping mapping, ActionForm form, HttpServletRequest request,
			HttpServletResponse response) {
		DepartmentForm departmentForm = (DepartmentForm) form;
		System.out.println("*** delete: " + "id: " + departmentForm.getDepartment().getDid() + ", name: " + departmentForm.getDepartment().getDname());
		return null;
	}
	
	public ActionForward update(ActionMapping mapping, ActionForm form, HttpServletRequest request,
			HttpServletResponse response) {
		DepartmentForm departmentForm = (DepartmentForm) form;
		System.out.println("*** update: " + "id: " + departmentForm.getDepartment().getDid() + ", name: " + departmentForm.getDepartment().getDname());
		return null;
	}
	
	public ActionForward list(ActionMapping mapping, ActionForm form, HttpServletRequest request,
			HttpServletResponse response) {
		DepartmentForm departmentForm = (DepartmentForm) form;
		System.out.println("*** list: " + "id: " + departmentForm.getDepartment().getDid() + ", name: " + departmentForm.getDepartment().getDname());
		return null;
	}
}

虽然这个时候可以实现分发处理,但是每一个方法上的参数实在是太多了。

那么下面不再建立表单页面,直接通过地址重写的方式传递:
(1)调用增加方法:
利用此类方式可以实现一个Action实现处理多种请求的问题,但是也需要明确的知道这种代码也会存在有一个缺陷:ActionForm怎么处理呢?
范例:明确的接收status参数处理

public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
		System.out.println(request.getParameter("status"));
		return null;
	}

如果真的是这样一个新的可怕的问题又出现了:
(1)每一个Action几乎都是多业务处理,那么每一个ActionForm都要去重复的编写大量的if...else,很不方便。

7 文件上传

只要一说到文件上传的操作,就应该立刻想到以下的几点问题:
(1)上传文件的表单一定要进行封装;
(2)需要使用一些上传组件,帮助用户简化上传的难度;
(3)对于被封装的表单与不封装的表单取得参数内容的操作形式不同。
如果你现在的项目使用的是纯粹的MVC开发(不使用开发框架),那么一定使用SmartUpload处理上传。但是如果使用的是开发框架(Struts、SpringMVC)都使用的是Apache的FileUpload上传组件完成的。并且为这个组件进行了足够多的封装操作,以简化用户的使用难度。

现在的Struts 1.x里面使用的也是FileUpload上传组件。

如果要想上传,那么在定义表单的时候要是哟个文件选择框以及表单封装。
范例:定义一个表单

<body>
    <html:form action="upload.do" method="post" focus="upload" enctype="multipart/form-data">
      <table border="0">
        <tr>
          <td>Name:</td>
          <td><html:text property="name" /></td>
        </tr>
        <tr> 
          <td>Photo:</td>
          <td><html:file property="photo" /></td>
        </tr>
        <tr>
          <td colspan="2" align="center">
          	<html:submit value="submit"/>
          	<html:reset value="reset"/>
          </td>
        </tr>
      </table>
    </html:form>
  </body>

此时还提高了一些操作难度,使用VO接收数据,但是对于文件的接收,它使用的类型应该是FormFile类型。
范例:定义UploadForm类

package org.lks.struts.form;

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.upload.FormFile;

@SuppressWarnings("serial")
public class UploadForm extends ActionForm {
	private String name;
	private FormFile photo;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public FormFile getPhoto() {
		return photo;
	}

	public void setPhoto(FormFile photo) {
		this.photo = photo;
	}

	public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
		request.getParameter("photo");
		return null;
	}

	public void reset(ActionMapping mapping, HttpServletRequest request) {
		// TODO Auto-generated method stub
	}
}

范例:定义UploadAction.java类,处理上传

package org.lks.struts.action;

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;
import org.lks.struts.form.UploadForm;

public class UploadAction extends Action {

	public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request,
			HttpServletResponse response) {
		UploadForm uploadForm = (UploadForm) form;
		System.out.println("[Name] " + uploadForm.getName());
		try {
			System.out.println("[Photo] name: " + uploadForm.getPhoto().getFileName()
					+ ", Size: " + uploadForm.getPhoto().getFileSize()
					+ ", Type: " + uploadForm.getPhoto().getContentType()
					+ ", Data: " + uploadForm.getPhoto().getFileData());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}

范例:手工配置struts-config.xml文件

<action attribute="uploadForm" input="/error.jsp" name="uploadForm"
			path="/upload" scope="request" type="org.lks.struts.action.UploadAction" 
			cancellable="true" />

此时就可以接收上传操作,并且可以通过字节数组取出所有的上传文件的内容,随后使用OutputStream保存。
范例:进行文件的保存

String fileName = UUID.randomUUID() + "." + uploadForm.getPhoto().getFileName().split("\\.")[1];
			String filePath = request.getServletContext().getRealPath("/") + fileName;
			File file = new File(filePath);
			System.out.println(filePath);
			InputStream input = uploadForm.getPhoto().getInputStream();
			OutputStream output = new FileOutputStream(file);
			int len = 0;
			byte[] data = new byte[1024];
			while((len = input.read(data)) != -1){
				output.write(data, 0, len);
			}
			input.close();
			output.close();

所有框架的保存操作几乎都是利用OutputStream与InputStream完成的。

8 Struts 1.x标签

在Struts里面有四种标签:html标签、bean标签、logic标签、tiles标签,所有的标签都会在建立jsp的时候默认创建好其标记:

<%@ 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" %>

范例:使用bean标签输出

//********************数据准备*********************
public ActionForward list(ActionMapping mapping, ActionForm form, HttpServletRequest request,
			HttpServletResponse response) {
		Department vo = new Department();
		vo.setDid(1001L);
		vo.setDname("SOFTWARE");
		request.setAttribute("department", vo);
		List<Department> allDepartments = new ArrayList<Department>();
		for(int i=0; i < 10; i++){
			Department department = new Department();
			department.setDid(1001L + i * 1L);
			department.setDname("SOFTWARE" + i);
			allDepartments.add(department);
		}
		request.setAttribute("departments", allDepartments);
		return mapping.findForward("list");
	}
//**************配置文件******************
<action attribute="departmentForm" input="/error.jsp" name="departmentForm"
			parameter="status" path="/department" scope="request"
			type="org.lks.struts.action.DepartmentAction" cancellable="true">
			<forward name="list" path="/list.jsp"></forward>	
		</action>

范例:观察bean的输出操作

<h1>标签输出:<bean:write name="department" property="dname"/></h1>
<h1>EL输出:${department.dname }</h1>

最早在没有EL的时代,<bean:write>标签很好用,其中name表示属性名称,而property表示的是VO类中的定义的类属性,但是有了EL之后,这种输出就不再使用了。
范例:读取资源文件

<h1>资源文件:<bean:message key="login.success"/></h1>

对于资源文件的读取操作是根据key的内容取出的。主要做国际化使用。

但是在正常情况下如果已经有EL之后不会再去使用bean标签,但是在struts 1.x里面这个logic标签主要负责判断以及循环输出的。
范例:循环输出集合的操作

<logic:present name="departments" scope="request">
    			<logic:iterate id="vo" name="departments">
    				<li>No.: ${vo.did }, Name: ${vo.dname }</li>
    			</logic:iterate>
    		</logic:present>

首先使用logic:present判断在request范围之中是否存在有departments的属性,在Struts里面继续使用了JSP中bean标签的格式,只要是有name的表示使用属性,而只要是id的表示定义新对象。

随后login:iterator表示迭代输出,里面的id表示每一个迭代出来的对象的名字,保存在page范围内,而name表示集合的属性名称。

严格来讲,Struts开发框架里面最有特点的就是html标签,因为这个标签具备有一个很强的回显操作。如果说现在要进行数据的更新操作,那么一定要将原始数据取出,去除之后在表单上进行回填,但是如果使用html标签这一切就可以简化了。
范例:处理更新前查询

//************************* Java ***************************
public ActionForward update(ActionMapping mapping, ActionForm form, HttpServletRequest request,
			HttpServletResponse response) {
		DepartmentForm departmentForm = (DepartmentForm) form;
		Department vo = new Department();
		vo.setDid(1001L);
		vo.setDname("SOFTWARE");
		departmentForm.setDepartment(vo);
		return mapping.findForward("update");
	}
//********************** jsp***********************
<html:form action="department.do" method="post" focus="department">
    	No.: <html:text property="department.did"/>
    	Name: <html:text property="department.dname"/>
    </html:form>

此时内容可以自动的设置到表单上,但是这样的操作现在基本上也用不到了。

实际上这种功能的强悍不仅于此。随着时间的发展,由于ActionForm的越来越不好,以及Struts的没落,这样的好处也被遮盖了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值