Struts2基础教程教程


- 1 - Struts2Struts2Struts2 Struts2 教程111 1 :第一个Struts2Struts2Struts2 Struts2 程序 在本系列教程中我们将学习到 Struts2的各种技术。在本教程中使用的工具和程序库的版本 如下: 开发工具:MyEclipse6 Web服务器:Tomcat6 Struts版本:Struts2.0.11.1 JDK版本:JDK1.5.0_12 J2EE版本:Java EE5.0 在本系列教程中Web工程的上下文路径都是struts2,如果在Web根目录有一个index.jsp 文件,则访问路径如下: http://localhost:8080/struts2/index.jsp 由于MyEclipse6目前并不支持Struts2,所以我们需要到struts.apache.org去下载Struts2 安装包。要想正常使用Struts2,至少需要如下五个包(可能会因为Struts2的版本不同,包 名略有差异,但包名的前半部是一样的)。 struts2-core-2.0.11.1.jar xwork-2.0.4.jar commons-logging-1.0.4.jar freemarker-2.3.8.jar ognl-2.6.11.jar Struts2虽然在大版本号上是第二个版本,但基本上在配置和使用上已经完全颠覆了 Struts1.x的方式(当然,Struts2仍然是基于MVC模式的,也是动作驱动的,可能这是唯一 没变的东西)。Struts2实际上是在Webwork基础上构建起来的MVC框架。我们从Struts2的 源代码中可以看到,有很多都是直接使用的xwork(Webwork的核心技术)的包。既然从技术 上来说Struts2是全新的框架,那么就让我们来学习一下这个新的框架的使用方法。 如果大家使用过Struts1.x,应该对建立基于Struts1.x的Web程序的基本步骤非常清楚。 让我们先来回顾一下建立基于Struts1.x的Web程序的基本步骤。 1. 安装Struts。由于Struts的入口点是ActionServlet,所以得在web.xml 中配置一下这 个Servlet。 2. 编写Action类(一般从org.apache.struts.action.Action类 继 承 )。 3. 编写ActionForm类(一般从org.apache.struts.action.ActionForm类继承),这一步 不是必须的,如果要接收客户端提交的数据,需要执行这一步。 4. 在struts-config.xml 文件中配置Action和ActionForm。 5. 如果要采集用户录入的数据,一般需要编写若干JSP页面,并通过这些JSP页面中 的form将数据提交给Action。 下面我们就按着编写struts1.x程序的这五步和struts2.x程序的编写过程一一对应,看看它们 - 2 - 谁更“酷”。下面我们来编写一个基于Struts2的Web程序。这个程序的功能是让用户录入两 个整数,并提交给一个StrutsAction,并计算这两个数的代数和,如果代码和为非负数,则 跳转到positive.jsp页面,否则跳转到negative.jsp页面。 【第111 1 步】 安装Struts2 这一步对于Struts1.x和Struts2都是必须的,只是安装的方法不同。Struts1的入口点是一 个Servlet,而Struts2的入口点是一个过滤器(Filter)。因此,Struts2要按过滤器的方式配置。 下面是在web.xml 中配置Struts2的代码: struts2 org.apache.struts2.dispatcher.FilterDispatcher struts2 /* 【第222 2 步】 编写Action类 这一步和 Struts1.x也必须进行。只是 Struts1.x中的动作类必须从 Action 类中继承,而 Struts2.x的动作类需要从com.opensymphony.xwork2.ActionSupport类继承。下面是计算两个整 数代码和的Action类,代码如下: package action; import com.opensymphony.xwork2.ActionSupport; public class FirstAction extends ActionSupport { private int operand1; private int operand2; public String execute() throws Exception { if (getSum() >= 0) // 如果代码数和是非负整数,跳到positive.jsp页面 { return "positive"; } else // 如果代码数和是负整数,跳到negative.jsp页面 { - 3 - return "negative"; } } public int getOperand1() { return operand1; } public void setOperand1(int operand1) { System.out.println(operand1); this.operand1 = operand1; } public int getOperand2() { return operand2; } public void setOperand2(int operand2) { System.out.println(operand2); this.operand2 = operand2; } public int getSum() { return operand1 + operand2; // 计算两个整数的代码数和 } } 从上面的代码可以看出,动作类的一个特征就是要覆盖execute方法,只是Struts2的execute 方法没有参数了,而Struts1.x的execute方法有四个参数。而且execute方法的返回值也不同 的。Struts2只返回一个String,用于表述执行结果(就是一个标志)。上面代码的其他部分 将在下面讲解。 【第333 3 步】 编写ActionForm类 在本例中当然需要使用ActionForm了 。在Struts1.x中,必须要单独建立一个ActionForm 类(或是定义一个动作Form),而在 Struts2中ActionForm和Action已经二合一了。从第二步 的代码可以看出,后面的部分就是应该写在ActionForm类中的内容。所以在第2步,本例的 ActionForm类已经编写完成(就是Action类的后半部分)。 【第444 4 步】 配置Action类 - 4 - 这一步 struts1.x和struts2.x都是必须 的, 只是 在 struts1.x中的配置 文件 一般 叫 struts- config.xml(当然也可以是其他的文件名),而且一般放到WEB-INF目录中。而在struts2.x中 的配置文件一般为struts.xml,放到WEB-INF"classes目录中。下面是在struts.xml中配置动作 类的代码: /positive.jsp /negative.jsp 标签中可以有多个,第一个可以指定一个Servlet访问路径 (不包括动作名),如“/mystruts”。extends属性继承一个默认的配置文件“struts-default”, 一般都继承于它,大家可以先不去管它。标签中的name属性表示动作名,class表 示动作类名。 标签的name实际上就是execute方法返回的字符串,如果返回的是“positive”, 就跳转到positive.jsp页面,如果是“negative”,就跳转到negative.jsp页面。在中 可以有多个,在中可以有多个。我们可以用如下的URL来访 问这个动作: http://localhost:8080/struts2/mystruts/sum.action 注:Struts1.x的动作一般都以.do结尾,而Struts2是以.action结尾。 【第555 5 步】 编写用户录入接口(JSP页面) 1.1.1. 1. 主界面(sum.jspsum.jspsum.jsp sum.jsp ) 在Web根目录建立一个sum.jsp,代码如下: <%@ page language="java" import="java.util.*" pageEncoding="GBK" %> <%@ taglib prefix="s" uri="/struts-tags"%> 输入操作数 - 5 - 求代数和
在sum.jsp中使用了Struts2带的tag。在Struts2中已经将Struts1.x的好几个标签库都统一了, 在Struts2中只有一个标签库/struts-tags。这里面包含了所有的Struts2标签。但使用Struts2 的标签大家要注意一下。在中最好都使用Struts2标签,尽量不要用HTML或普通文 本,大家可以将sum.jsp的代码改为如下的形式,看看会出现什么效果: ...... 求代数和
操作数1:
操作数2:
...... 提示一下,在中Struts2使用定位。 2.2.2. 2. positive.jsppositive.jsppositive.jsp positive.jsp <%@ page language="java" import="java.util.*" pageEncoding="GBK"%> <%@ taglib prefix="s" uri="/struts-tags" %> 显示代数和 代数和为非负整数

3.3.3. 3. negative.jspnegative.jspnegative.jsp negative.jsp - 6 - <%@ page language="java" import="java.util.*" pageEncoding="GBK"%> <%@ taglib prefix="s" uri="/struts-tags" %> 显示代数和 代数和为负整数

这两个jsp页面的实现代码基本一样,只使用了一个标签来显示Action类中 的sum属性值。标签是从request对象中获得了一个对象中得到的sum属性,如 我们可以使用如下的代码来代替: <% com.opensymphony.xwork2.util.OgnlValueStack ovs = (com.opensymphony.xwork2.util.OgnlValueStack)request.getAttribute("struts.valueStack") ; out.println(ovs.findString("sum")); %> 启动Tomcat后,在IE中输入如下的URL来测试这个例子: http://localhost:8080/struts2/sum.jsp 下一篇:Struts2教程2:处理一个form多个submit Struts2Struts2Struts2 Struts2 教程222 2 :处理一个formformform form 多个submitsubmitsubmit submit 在很多Web应用中,为了完成不同的工作,一个HTML form标签中可能有两个或多个submit 按钮,如下面的代码所示: - 7 - 由于在
中的多个提交按钮都向一个action提交,使用Struts2 Action的execute方法就 无法判断用户点击了哪一个提交按钮。如果大家使用过Struts1.x就会知道在Struts1.2.9之前 的版本需要使用一个 LookupDispatchAction动作来处理含有多个 submit的form。但使用 LookupDispatchAction动作需要访问属性文件,还需要映射,比较麻烦。从Struts1.2.9开始 , 加入了一个EventDispatchAction动作。这个类可以通过java反射来调用通过request参数指 定的动作(实际上只是判断某个请求参数是不存在,如果存在,就调用在action类中和这个 参数同名的方法)。使用EventDispatchAction必须将submit的name属性指定不同的值以区 分每个submit。而在Struts2中将更容易实现这个功能。 当然,我们也可以模拟EventDispatchAction的方法通过request获得和处理参数信息。但这 样比较麻烦。在Struts2中提供了另外一种方法,使得无需要配置可以在同一个action类中执 行不同的方法(默认执行的是execute方法)。使用这种方式也需要通过请求参来来指定要执 行的动作。请求参数名的格式为 action!method.action 注:由于Struts2只需要参数名,因此,参数值是什么都可以。 下面我就给出一个实例程序来演示如何处理有多个submit的form: 【第1步】实现主页面(more_submit.jsp) <%@ page language="java" import="java.util.*" pageEncoding="GBK"%> <%@ taglib prefix="s" uri="/struts-tags" %> My JSP'hello.jsp' starting page 在more_submit.jsp中有两个submit:保存和打印。其中分别通过method属性指定了要调用 的方法:save和print。因此,在Action类中必须要有save和print方法。 【第2步】实现Action类(MoreSubmitAction) package action; - 8 - import javax.servlet.http.*; import com.opensymphony.xwork2.ActionSupport; import org.apache.struts2.interceptor.*; public class MoreSubmitAction extends ActionSupport implements ServletRequestAware { private String msg; private javax.servlet.http.HttpServletRequest request; // 获得HttpServletRequest对象 public void setServletRequest(HttpServletRequest request) { this.request = request; } // 处理save submit按钮的动作 public String save() throws Exception { request.setAttribute("result", "成功保存[" + msg + "]"); return "save"; } // 处理print submit按钮的动作 public String print() throws Exception { request.setAttribute("result", "成功打印[" + msg + "]"); return "print"; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } } 上面的代码需要注意如下两点: save和print方法必须存在,否则会抛出java.lang.NoSuchMethodException异常。 Struts2 Action动作中的方法和Struts1.x Action的execute不同,只使用Struts2 Action动作的 execute方法无法访问request对象,因此,Struts2 Action类需要实现一个Struts2自带的拦 截器来获得request对象,拦截器如下: - 9 - org.apache.struts2.interceptor. ServletRequestAware 【第3步】配置Struts2 Action struts.xml的代码如下: /result.jsp /result.jsp 【第4步】编写结果页(result.jsp) <%@ page pageEncoding="GBK"%> 提交结果

${result}

在result.jsp中将在save和print方法中写到request属性中的执行结果信息取出来,并输出到 客户端。 启动Tomcat后,在IE中执行如下的URL来测试程序: http://localhost:8080/moresubmit/more_submit.jsp 大家也可以直接使用如下的URL来调用save和print方法: 调用save方法:http://localhost:8080/moresubmit/submit!save.action 调用print方法:http://localhost:8080/moresubmit/submit!print.action - 10 - 源代码:http://www.itpub.net/attachment.php?aid=520773 Struts2Struts2Struts2 Struts2 教程333 3 :struts.xmlstruts.xmlstruts.xml struts.xml 常用配置解析 在本文中将详细讲述struts.xml 文件的常用配置及注意事项。 1.1.1. 1. 使用 标签重用配置文件 在Struts2中提供了一个默认的 struts.xml文件,但如果 package、action、interceptors 等配置比较多时,都放到一个 struts.xml文件不太容易维护。因此,就需要将 struts.xml 文件分成多个配置文件,然后在 struts.xml文件中使用标签引用这些配置文件。 这样做的优点如下: 结构更清晰,更容易维护配置信息。 配置文件可以复用。如果在多个 Web程序中都使用类似或相同的配置文件,那么可以使用 标签来引用这些配置文件,这样可以减少工作量。 假设有一个配置文件,文件名为newstruts.xml ,代码如下: /result.jsp /result.jsp 则struts.xml引用newstruts.xml文件的代码如下: - 11 - 大家要注意一下, 用 引用的 xml 文件也必须是完成 的 struts2的配置。实际上 在引用时是单独解析的xml 文件,而不是将被引用的文件插入到struts.xml 文件中。 2.2.2. 2. actionactionaction action 的别名 在默认情况下,Struts2会调用动作类的execute方法。但有些时候,我们需要在一个动作 类中处理不同的动作。也就是用户请求不同的动作时,执行动作类中的不同的方法。为了达 到这个目的,可以在标签中通过method方法指定要指行的动作类的方法名,并且需 要为不同的动作起不同的名子(也称为别名)。如下面代码所示: 上面代码的两个动作的class属性都指向同一个类,name为这个类起了两个动作别名:test 和my。在动作my中,使用了method属性指定要要运行的方法名为my。 在MyAction类中必须要有my方法,代码如下: package action; import com.opensymphony.xwork2.ActionSupport; public class MyAction extends ActionSupport { public String execute() throws Exception { // 处理test动作的代码 - 12 - } public String my() throws Exception { // 处理my动作的代码 } } 除了在struts.xml 中配置别名,还可以通过请求参数来描述指定动作(并不需要在struts.xml 中配置)。请求参数的格式如下: http://localhost:8080/contextPath/actionName!method.action 关于通过请求指定动作的详细内容,请参阅笔者写的 《Struts2教程2:处理一个form多个 submit》。 3.3.3. 3. 为actionactionaction action 指定参数 在struts2中还可以为action指定一个或多个参数。大家还记着 struts1.x是如何设置的action 参数不? 在struts1.x中可以使用 标签的parameter属性为其指定一个 action参 数 , 如果要指定多个,就只能通过逗号(,)或其他的分隔符将不同的参数隔开。而在struts2中 可以通过标签指定任意多个参数。代码如下: value1 value2 /result.jsp 当然,在action中读这些参数也非常简单,只需要象获取请求参数一样在action类中定义 相应的setter方法即可(一般不用定义getter方法)。如下面的代码将读取param1和param2参数 的值: package action; import com.opensymphony.xwork2.ActionSupport; public class MyAction extends ActionSupport { private String param1; private String param2; - 13 - public String execute() throws Exception { System.out.println(param1 + param2); } public void setParam1(String param1) { this.param1 = param1; } public void setParam2(String param2) { this.param2 = param2; } } 当struts2在调用execute之前,param1和param2的值就已经是相应参数的值了,因此,在 execute方法中可以直接使用param1和param2。 4.4.4. 4. 选择resultresultresult result 类型 在默认时,标签的type属性值是“dispatcher”(实际上就是转发,forward)。开 发 人员可以根据自己的需要指定不同的类型,如redirect、stream等。如下面代码所示: /result.jsp 这此result-type可以在 struts2-core-2.0.11.1.jar包或struts2源代码中的 struts-default.xml 文 件中找到,在这个文件中找到标签,所有的result-type都在里面定义了。代码 如下: 5.5.5. 5. 全局resultresultresult result 有很多时候一个初很多使用,这时可以使用标签来定义全 局的,代码如下: /result.jsp 如果中没有相应的,Struts2就会使用全局的。 Struts2Struts2Struts2 Struts2 教程444 4 :使用validatevalidatevalidate validate 方法验证数据 在Struts2 中最简单的验证数据的方法是使用validate。我们从ActionSupport类的 源代码中可以看到,ActionSupport类实现了一个Validateable接口。这个接口只 有一个validate方法。如果Action类实现了这个接口,Struts2 在调用execute 方法 之前首先会调用这个方法,我们可以在validate方法中验证,如果发生错误,可 以根据错误的level选择字段级错误,还是动作级错误。并且可使用addFieldError 或addActionError加入相应的错误信息,如果存在Action或Field错误,Struts2 会 返 回“input”(这个并不用开发人员写,由Struts2 自动返回),如果返回了“input”, - 15 - Struts2 就不会再调用execute 方法了。如果不存在错误信息,Struts2 在最后会调 用execute 方法。 这两个add方法和ActionErrors类中的add方法类似,只是add方法的错误信息需 要一个ActionMessage对象,比较麻烦。除了加入错误信息外,还可以使用 addActionMessage方法加入成功提交后的信息。当提交成功后,可以显示这些 信息。 以上三个add方法都在ValidationAware接口中定义,并且在ActionSupport类中有一个默认的实 现。其实,在ActionSupport类中的实现实际上是调用了ValidationAwareSupport中的相应的方 法,也就是这三个add方法是在ValidationAwareSupport类中实现的,代码如下: private final ValidationAwareSupport validationAware = new ValidationAwareSupport(); public void addActionError(String anErrorMessage) { validationAware.addActionError(anErrorMessage); } public void addActionMessage(String aMessage) { validationAware.addActionMessage(aMessage); } public void addFieldError(String fieldName, String errorMessage) { validationAware.addFieldError(fieldName, errorMessage); } 下面我们来实现一个简单的验证程序,来体验一个validate方法的使用。 先来在Web根目录建立一个主页面(validate.jsp),代码如下: <%@ page language="java" import="java.util.*" pageEncoding="GBK"%> <%@ taglib prefix="s" uri="/struts-tags" %> 验证数据 输入内容:
- 16 - 在上面的代码中,使用了Struts2 的tag :,分别用来显示动作错误信息,字段错误信息,和动作信息。如果信息 为空,则不显示。 现在我们来实现一个动作类,代码如下: package action; import javax.servlet.http.*; import com.opensymphony.xwork2.ActionSupport; import org.apache.struts2.interceptor.*; public class ValidateAction extends ActionSupport { private String msg; public String execute() { System.out.println(SUCCESS); return SUCCESS; } public void validate() { if(!msg.equalsIgnoreCase("hello")) { System.out.println(INPUT); this.addFieldError("msg.hello", "必须输入hello!"); this.addActionError("处理动作失败!"); } else { this.addActionMessage("提交成功"); } } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } } - 17 - 大家从上面的代码可以看出, Field错误需要一个 key(一般用来表示是哪一个属性出的错 误 ),而 Action错误和Action消息只要提供一个信息字符串就可以了。 最后来配置一下这个Action,代码如下: /error/validate.jsp /error/validate.jsp 假设应用程序的上下文路径为demo,则可通过如下的URL来测试程序: http://localhost:8080/demo/validate.jsp 我们还可以使用ValidationAware接口的其他方法(由ValidationAwareSupport类实现)获得 或设置字段错误信息、动作错误信息以及动作消息。如 hasActionErrors方法判断是否存在 动作层的错误,getFieldErrors获得字段错误信息(一个Map对象)。下面是ValidationAware 接口提供的所有的方法: package com.opensymphony.xwork2; import java.util.Collection; import java.util.Map; public interface ValidationAware { void setActionErrors(Collection errorMessages); Collection getActionErrors(); void setActionMessages(Collection messages); Collection getActionMessages(); void setFieldErrors(Map errorMap); Map getFieldErrors(); void addActionError(String anErrorMessage); void addActionMessage(String aMessage); void addFieldError(String fieldName, String errorMessage); boolean hasActionErrors(); boolean hasActionMessages(); boolean hasErrors(); boolean hasFieldErrors(); } - 18 - Struts2Struts2Struts2 Struts2 教程555 5 :使用ValidationValidationValidation Validation 框架验证数据 在《Struts2教程4:使用validate方法验证数据》中曾讲到使用validate方法来验证客户端提 交的数据,但如果使用validate方法就会将验证代码和正常的逻辑代码混在一起,但这样做 并不利于代码维护,而且也很难将过些代码用于其他程序的验证。在 Struts2中为我们提供 了一个Validation框架,这个框架和Struts1.x提供的Validation框架类似,也是通过XML 文件 进行配置。 一、服务端验证 下面将给出一个例子来演示如何使用Struts2的validation框架来进行服务端验证。我们可以 按着如下四步来编写这个程序: 【第111 1 步】建立Action类(NewValidateAction.java) package action; import com.opensymphony.xwork2.ActionSupport; public class NewValidateAction extends ActionSupport { private String msg; // 必须输入 private int age; // 在13和20之间 public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } 下面我们来验证msg和age属性。 【第222 2 步】配置Action类,struts.xml 的代码如下: - 19 - /validate_form.jsp /validate_form.jsp 【第333 3 步】编写验证规则配置文件 这是一个基于 XML 的配置文件,和 struts1.x中的validator框架的验证规则配置文件类 似。但一般放到和要验证的.class文件在同一目录下,而且配置文件名要使用如下两个规则 中的一个来命名: -validation.xml --validation.xml 其中 就是struts.xml 中的name属性值。在本例中我们使用第一种 命名规则,所以文件名是NewValidateAction-validation.xml 。文件的内容如下: 请输入信息 13 20 必须在 13至20之间 - 20 - 这个文件使用了两个规则:requiredstring(必须输入)和int(确定整型范围)。关于其他更 详细的验证规则,请读者访问http://struts.apache.org/2.0.11.1/docs/validation.html来查看。 【第444 4 步】编写数据录入JSP页。 在Web根目录中建立一个validate_form.jsp文件,代码如下: <%@ page language="java" import="java.util.*" pageEncoding="GBK"%> <%@ taglib prefix="s" uri="/struts-tags" %> "> 验证数据 大家要注意一下,如果在struts.xml 的标签中指定 namespace属性,需要在 中也将namespace和action分开写,如上面代码所示。不能将其连在一起,Struts2 需要分开的action和namespace。如下面的代码是错误的: ...... 在上面的程序中还使用了一个styles.css来定制错误信息的风格。代码如下: .label {font-style:italic; } .errorLabel {font-style:italic; color:red; } .errorMessage {font-weight:bold; color:red; } 需要在Web根目录中建立一个styles目录,并将styles.css 假设Web工程的上下文路径是validation,可以使用如下的URL来测试这个程序: http://localhost:8080/validation/validate_form.jsp 显示结果如图1所示。 - 21 - 图1 二、客户端验证 在Struts2中实现客户端验证非常简单,只需要在 中加入一个 validate属性,值为 true。如 ... 即可。 三、验证嵌套属性 有一类特殊的属性,即这个属性的类型是另外一个JavaBean,如有一个User类,代码如 下: package data; public class User { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) - 22 - { this.age = age; } } 在NewValidateAction类中加一个user属性,代码如下: package action; import com.opensymphony.xwork2.ActionSupport; import data.User; public class NewValidateAction extends ActionSupport { private String msg; private int age; private User user; public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } } - 23 - 如果要验证NewValidateAction中的user属性,可以使用visitor验证器。操作过程如下: 首先在NewValidateAction-validation.xml 中加入一个标签,代码如下: abc true User: 其中context参数将作为验证User类属性的文件名的一部分,如user属性返回一个User对象, 那么用于验证User对象属性的文件名为 User-abc-validation.xml。这个文件要和User.class 文件在同一个目录中。appendPrefix表示是否在字段里加user,如果为true,Struts2就会使 用user.name在form提交的数据中查找要验证的数据。这个属性的默认值是true。如果出错, Struts2会将标签中的信息加到 User-abc-validation.xml 文件中的相应错误信息 前面。 User-abc-validation.xml文件的内容如下: 请输入name 5 20 必须在 5至20之间 - 24 - 下面修改validate_form.jsp,代码如下: 大家可以看到,最后两个的name属性是user.name 和user.age,正 好是加了前缀的。 现在重新访问 http://localhost:8080/validation/validate_form.jsp,验证界面如图2所示。 图2 经笔者测试,使用visitor无法以客户端验证的方式来验证user属性,但NewValidateAction 中其他的属性可以使用客户端测试。 Struts2Struts2Struts2 Struts2 教程666 6 :在ActionActionAction Action 类中获得HttpServletResponseHttpServletResponseHttpServletResponse HttpServletResponse 对象 - 25 - 的四种方法 在struts1.x Action类的execute方法中,有四个参数,其中两个就是response和request。而 在Struts2 中,并没有任何参数,因此,就不能简单地从execute 方法获得 HttpServletResponse或HttpServletRequest对象了。 但在Struts2 Action类中仍然有很多方法可以获得这些对象。下面就列出四种获得这些 对象的方法。 【方法111 1 】使用Struts2Struts2Struts2 Struts2 AwareAwareAware Aware 拦截器 这种方法需要Action类实现相应的拦截器接口。如我们要获得 HttpServletResponse对 象,需要实现org.apache.struts2.interceptor.ServletResponseAware接口,代码如下: package action; import com.opensymphony.xwork2.ActionSupport; import javax.servlet.http.*; import org.apache.struts2.interceptor.*; public class MyAction extends ActionSupport implements ServletResponseAware { private javax.servlet.http.HttpServletResponse response; // 获得HttpServletResponse对象 public void setServletResponse(HttpServletResponse response) { this.response = response; } public String execute() throws Exception { response.getWriter().write("实现ServletResponseAware接口"); } } 在上面的代码中,MyAction 实现了一个ServletResponseAware 接口,并且实现了 setServletResponse方法。如果一个动作类实现了ServletResponseAware接口,Struts2在 调用execute方法之前,就会先调用setServletResponse方法,并将response参数传入这个 方法。如果想获得 HttpServletRequest、HttpSession和Cookie等对象,动作类可以分别实 现ServletRequestAware 、SessionAware 和CookiesAware 等接口。这些接口都在 org.apache.struts2.interceptor包中。 如果要获得请求参数,动作类可以实现 org.apache.struts2.interceptor. ParameterAware接 口,但如果只想判断某个参数是否存在,也可以实现com.opensymphony.xwork2.interceptor. - 26 - ParameterNameAware接口。这个接口有一个acceptableParameterName方法,当Struts2 获得一个请求参数时,就会调用一次。读者可以在这个方法中将所有的请求参数记录下来, 以便以后使用。这个方法的定义如下: boolean acceptableParameterName(String parameterName); 【方法222 2 】使用RequestAwareRequestAwareRequestAware RequestAware 拦截器 这种方法和第1种方法类似。动作类需要实现一个 org.apache.struts2.interceptor.RequestAware接口。所不同的是RequestAware将获得一个 com.opensymphony.xwork2.util.OgnlValueStack对象,这个对象可以获得response、 request及其他的一些信息。代码如下所示: package action; import java.util.Map; import org.apache.struts2.*; import com.opensymphony.xwork2.ActionSupport; import javax.servlet.http.*; import com.opensymphony.xwork2.util.*; import org.apache.struts2.interceptor.*; public class FirstAction extends ActionSupport implements RequestAware { private Map request; private HttpServletResponse response; public void setRequest(Map request) { this.request = request; } public String execute() throws Exception { java.util.Set keys = request.keySet(); // 枚举所有的key值。实际上只有一个key:struts.valueStack for(String key: keys) System.out.println(key); // 获得OgnlValueStack 对象 OgnlValueStack stack = (OgnlValueStack)myRequest.get("struts.valueStack"); // 获得HttpServletResponse对象 response = (HttpServletResponse)stack.getContext().get(StrutsStatics.HTTP_RESPONSE); response.getWriter().write("实现RequestAware 接口"); } } - 27 - 我们也可以使用StrutsStatics.HTTP_REQUEST、StrutsStatics.PAGE_CONTEXT 来获 得HttpServletRequest和PageContext对象。这种方法有些麻烦,一般很少用,读者可以作 为一个参考。 【方法333 3 】使用ActionContextActionContextActionContext ActionContext 类 这种方法比较简单,我们可以通过org.apache.struts2.ActionContext类的get方法获得相应 的对象。代码如下: HttpServletResponse response(HttpServletResponse) = ActionContext.getContext().get(org.apache.struts2.StrutsStatics.HTTP_RESPONSE); HttpServletRequest request(HttpServletRequest) = ActionContext.getContext().get(org.apache.struts2.StrutsStatics.HTTP_REQUEST); 【方法444 4 】使用ServletActionContextServletActionContextServletActionContext ServletActionContext 类 Struts2为我们提供了一种最简单的方法获得HttpServletResponse及其他对象。这就是 org.apache.struts2.ServletActionContext类。我们可以直接使用 ServletActionContext类的 getRequest、getResponse方法来获得 HttpServletRequest、HttpServletResponse对 象 。 代码如下: HttpServletResponse response = ServletActionContext.getResponse() response.getWriter().write("hello world"); 从这四种方法来看,最后一种是最简单的,读者可以根据自己的需要和要求来选择使用 哪一种方法来获得这些对象。 Struts2Struts2Struts2 Struts2 教程777 7 :上传任意多个文件 一、上传单个文件 上传文件是很多Web程序都具有的功能。在Struts1.x中已经提供了用于上传文件的组件。 而在Struts2中提供了一个更为容易操作的上传文件组件。所不同的是, Struts1.x的上传组 件需要一个ActionForm来传递文件,而Struts2的上传组件是一个拦截器(这个拦截器不用 配置,是自动装载的)。在本文中先介绍一下如何用struts2上传单个文件,最后介绍一下用 struts2上传任意多个文件。 要用Struts2实现上传单个文件的功能非常容易实现,只要使用普通的Action即可。但为 了获得一些上传文件的信息,如上传文件名、上传文件类型以及上传文件的 Stream对 象 , 就需要按着一定规则来为Action类增加一些getter和setter方法。 在Struts2中,用于获得和设置 java.io.File对象(Struts2将文件上传到临时路径,并使 用java.io.File打开这个临时文件)的方法是getUpload和setUpload。获得和设置文件名的方 法是getUploadFileName和setUploadFileName ,获得和设置上传文件内容类型的方法是 getUploadContentType和setUploadContentType。下面是用于上传的动作类的完整代码: - 28 - package action; import java.io.*; import com.opensymphony.xwork2.ActionSupport; public class UploadAction extends ActionSupport { private File upload; private String fileName; private String uploadContentType; public String getUploadFileName() { return fileName; } public void setUploadFileName(String fileName) { this.fileName = fileName; } public File getUpload() { return upload; } public void setUpload(File upload) { this.upload = upload; } public void setUploadContentType(String contentType) { this.uploadContentType=contentType; } public String getUploadContentType() { return this.uploadContentType; } public String execute() throws Exception { java.io.InputStream is = new java.io.FileInputStream(upload); - 29 - java.io.OutputStream os = new java.io.FileOutputStream("d:\\upload\\" + fileName); byte buffer[] = new byte[8192]; int count = 0; while((count = is.read(buffer)) > 0) { os.write(buffer, 0, count); } os.close(); is.close(); return SUCCESS; } } 在execute方法中的实现代码就很简单了,只是从临时文件复制到指定的路径(在这里 是d:\upload)中。上传文件的临时目录的默认值是javax.servlet.context.tempdir的值,但可 以通过struts.properties(和struts.xml 在同一个目录下)的struts.multipart.saveDir属性设置 。 Struts2上传文件的默认大小限制是 2M(2097152字节),也可以通过struts.properties文件 中的struts.multipart.maxSize修改,如struts.multipart.maxSize=2048 表示一次上传文件的 总大小不能超过2K字节。 下面的代码是上传文件的JSP页面代码: <%@ page language="java" import="java.util.*" pageEncoding="GBK"%> <%@ taglib prefix="s" uri="/struts-tags"%> 上传单个文件 也可以在success.jsp页中通过获得文件的属性(文件名和文件内容类型),代 码如下: - 30 - 二、上传任意多个文件 在Struts2中,上传任意多个文件也非常容易实现。首先,要想上传任意多个文件,需要 在客户端使用DOM技术生成任意多个标签。name属性值都相同。代 码如下:

上面的javascript代码可以生成任意多个 标签,name的值都为file (要注意的是,上面的javascript代码只适合于IE浏览器,firefox等其他浏览器需要使用他的 代 码 )。至于Action类,和上传单个文件的Action类基本一至,只需要将三个属性的类型改为 List即可。代码如下: package action; import java.io.*; import com.opensymphony.xwork2.ActionSupport; public class UploadMoreAction extends ActionSupport { - 31 - private java.util.List uploads; private java.util.List fileNames; private java.util.List uploadContentTypes; public java.util.List getUploadFileName() { return fileNames; } public void setUploadFileName(java.util.List fileNames) { this.fileNames = fileNames; } public java.util.List getUpload() { return uploads; } public void setUpload(java.util.List uploads) { this.uploads = uploads; } public void setUploadContentType(java.util.List contentTypes) { this.uploadContentTypes = contentTypes; } public java.util.List getUploadContentType() { return this.uploadContentTypes; } public String execute() throws Exception { if (uploads != null) { int i = 0; for (; i < uploads.size(); i++) { java.io.InputStream is = new java.io.FileInputStream(uploads.get(i)); java.io.OutputStream os = new java.io.FileOutputStream( "d:\\upload\\" + fileNames.get(i)); byte buffer[] = new byte[8192]; - 32 - int count = 0; while ((count = is.read(buffer)) > 0) { os.write(buffer, 0, count); } os.close(); is.close(); } } return SUCCESS; } } 在execute方法中,只是对List对象进行枚举,在循环中的代码和上传单个文件时的代码 基本相同。如果读者使用过struts1.x的上传组件,是不是感觉Struts2的上传功能更容易实现 呢?在Struts1.x中上传多个文件时,可是需要建立带索引的属性的。而在Struts2中,就是这 么简单就搞定了。图1是上传任意多个文件的界面。 图1 在本文中给出了用Struts2上传任意多个文件的一个方法,如果哪位读者有更好的方法, 请跟贴! - 33 - Struts2Struts2Struts2 Struts2 教程888 8 :拦截器概述 Struts2的拦截器和Servlet过滤器类似。在执行Action的execute 方法之前,Struts2 会首先执行在 struts.xml中引用的拦截器,在执行完所有引用的拦截器的 intercept方法后,会执行Action的execute 方法。 Struts2 拦截器类必须从com.opensymphony.xwork2.interceptor.Interceptor接口 继承,在Intercepter接口中有如下三个方法需要实现: void destroy(); void init(); String intercept(ActionInvocation invocation) throws Exception; 其中intercept方法是拦截器的核心方法,所有安装的拦截器都会调用之个方法。 在Struts2 中已经在struts-default.xml中预定义了一些自带的拦截器,如timer、 params等。如果在标签中继承struts-default,则当前package就会自 动拥有struts-default.xml中的所有配置。代码如下: ... 在struts-default.xml中有一个默认的引用,在默认情况下(也就是 中未引用拦 截器时)会自动引用一些拦截器。这个默认的拦截器引用如下: - 34 - dojo\..* input,back,cancel,browse input,back,cancel,browse 上面在 defaultStack中引用的拦截器都可以在 中不经过引用就可以使用(如果在 中引用了任何拦截器后,要使用在defaultStack中定义的拦截器,也需要在 中重新引用,在后面将详细讲解)。 下面我们来看几个简单的拦截器的使用方法。 一、记录拦截器和executeexecuteexecute execute 方法的执行时间(timer)(timer)(timer) (timer) timer 是Struts2 中最简单的拦截器,这个拦截器对应的类是 com.opensymphony.xwork2.interceptor.TimerInterceptor。它的功能是记录execute方法和其他拦 截器(在timer后面定义的拦截器)的intercept方法执行的时间总和。如下面的配置代码所示 : 由于在timer后面没有其他的拦截器定义,因此,timer只能记录execute方法的执行时间,在 访问first动作时,会在控制台输出类似下面的一条信息: 信息: Executed action [/test/first!execute] took 16 ms. 在使用timer拦截器时,需要commons-logging.jar的支持。将logger引用放到timer的后面, 就可以记录logger拦截器的intercept方法和Action的execute方法的执行时间总和,代码如下: 大家可以使用如下的Action类来测试一下timer拦截器: package action; - 35 - import com.opensymphony.xwork2.ActionSupport; public class FirstAction extends ActionSupport { public String execute() throws Exception { Thread.sleep(1000); // 延迟1秒 return null; } } 如果只记录execute方法的执行时间,一般会输出如下的信息: 信息: Executed action [/test/first!execute] took 1000 ms. 二、通过请求调用ActionActionAction Action 的settersettersetter setter 方法(params)(params)(params) (params) 当客户端的一个form向服务端提交请求时,如有一个textfield,代码如下: 在提交后,Struts2将会自动调用first动作类中的setName方法,并将name文本框中的值通 过setName方法的参数传入。实际上,这个操作是由params拦截器完成的,params对应的 类是 com.opensymphony.xwork2.interceptor.ParametersInterceptor。由于 params已经在 defaultStack中定义,因此,在未引用拦截器的中是会自动引用params的,如下面 的配置代码,在访问first动作时,Struts2是会自动执行相应的setter方法的。 ...... 但如果在中引用了其他的拦截器,就必须再次引用params拦截器,Struts2才能调 用相应的setter方法。如下面的配置代码所示: - 36 - 三、通过配置参数调用ActionActionAction Action 的settersettersetter setter 方法(static-params)(static-params)(static-params) (static-params) static-params拦截器可以通过配置 标签来调用 Action 类的相应的 setter方法, static-params 拦截器对应的类是 com.opensymphony.xwork2.interceptor.StaticParametersInterceptor。 下面配置代码演示了如何使用static-params拦截器: 比尔 如果first动作使用上面的配置,在访问first动作时,Struts2会自动调用setWho方法将“比尔” 作为参数值传入setWho方法。 四、使用拦截器栈 为了能在多个动作中方便地引用同一个或几个拦截器,可以使用拦截器栈将这些拦截器 作为一个整体来引用。拦截器栈要在 标签 中 使 用 和子 标 签 来定义。代码如下: 比尔 可以象使用拦截器一样使用拦截器栈,如上面代码所示。 Struts2Struts2Struts2 Struts2 教程999 9 :实现自已的拦截器 在上一篇中介绍了Struts2拦截器的原理,在这一篇中我们将学习一下如何编写自己的拦截 - 37 - 器。 一、拦截器的实现 实现一个拦截器非常简单。实际上,一个拦截器就是一个普通的类,只是这个类必须实 现com.opensymphony.xwork2.interceptor.Interceptor接口。Interceptor接口有如下三个方法: public interface Interceptor extends Serializable { void destroy(); void init(); String intercept(ActionInvocation invocation) throws Exception; } 其中init和destroy方法只在拦截器加载和释放(都由 Struts2自身处理)时执行一次。而 intercept方法在每次访问动作时都会被调用。Struts2在调用拦截器时,每个拦截器类只有一 个对象实例,而所有引用这个拦截器的动作都共享这一个拦截器类的对象实例,因此,在实 现Interceptor接口的类中如果使用类变量,要注意同步问题。 下面我们来实现一个简单的拦截器,这个拦截器通过请求参数action指定一个拦截器类中的 方法,并调用这个方法(我们可以使用这个拦截器对某一特定的动作进行预处理)。如果方 法不存在,或是action参数不存在,则继续执行下面的代码。如下面的URL: http://localhost:8080/struts2/test/interceptor.action?action=test 访问上面的url后,拦截器会就会调用拦截器中的 test方法,如果这个方法不存在,则调用 invocation.invoke方法, invoke方法和 Servlet过滤器中调用 FilterChain.doFilter方法类似, 如果在当前拦截器后面还有其他的拦截器,则 invoke方法就是调用后面拦截器的 intercept 方法,否则,invoke会调用Action类的execute方法(或其他的执行方法)。 下面我们先来实现一个拦截器的父类 ActionInterceptor。这个类主要实现了根据action参数 值来调用方法的功能,代码如下: package interceptor; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; import javax.servlet.http.*; import org.apache.struts2.*; public class ActionInterceptor implements Interceptor { protected final String INVOKE = "##invoke"; - 38 - public void destroy() { System.out.println("destroy"); } public void init() { System.out.println("init"); } public String intercept(ActionInvocation invocation) throws Exception { HttpServletRequest request = ServletActionContext.getRequest(); String action = request.getParameter("action"); System.out.println(this.hashCode()); if (action != null) { try { java.lang.reflect.Method method = this.getClass().getMethod(action); String result = (String)method.invoke(this); if(result != null) { if(!result.equals(INVOKE)) return result; } else return null; } catch (Exception e) { } } return invocation.invoke(); } } 从上面代码中的intercept方法可以看出,在调用action所指定的方法后,来判断返回值。可 能发生的情况有三种: 1. 返回值为null,执行return null。 2. 返回值为INVOKE,执行return invockation.invoke()。 3. 其他情况,执行return result。 result表示指定方法的返回值,如上面代码所示。 在实现完上面的拦截器父类后,任何继承于ActionInterceptor类的拦截器都可以自动根 - 39 - 据action 的参数值调用自身的相应方法。下面我们来实现一个拥有两个动作方法 test和print 的拦截器类。代码如下: package interceptor; import javax.servlet.http.HttpServletResponse; import org.apache.struts2.ServletActionContext; public class MultiMethodInterceptor extends ActionInterceptor { public String test() throws Exception { HttpServletResponse response = ServletActionContext.getResponse(); response.getWriter().println("invoke test"); return this.INVOKE; } public String print() throws Exception { HttpServletResponse response = ServletActionContext.getResponse(); response.getWriter().println("invoke print"); return null; } } test方法返回了 INVOKE,因此,在执行完这个方法后, Struts2会接着调用其他拦截器的 intercept方法或Action类的execute方法。而print方法在执行完后,只是返回了null,而不再 调用其他的方法了,也就是访问如下的url时,动作的execute方法将不会执行: http://localhost:8080/struts2/test/ddd.action?action=print 下面我们来实现一个Action类,代码如下: package action; import org.apache.struts2.*; import com.opensymphony.xwork2.ActionSupport; public class InterceptorAction extends ActionSupport { public String abcd() throws Exception { - 40 - ServletActionContext.getResponse().getWriter() .println("invoke abcd"); return null; } } 在这个Action类中,只有一个abcd方法,实际上,这个方法相当于execute方法,在下面会 设置动作的method属性为abcd。下面我们来在struts.xml 中定义拦截器类和动作,代码如下 : 在配置上面的methodStack拦截器时要注意,最好在后面引用defaultStack,否则很多通过 拦截器提供的功能将失去。 OK,现在访问如下的URL: http://localhost:8080/struts2/test/ddd.action?action=test 在浏览器中将会出现如下的字符串: invoke test invoke abcd 而如果访问http://localhost:8080/struts2/test/ddd.action?action=print,将会只出现如下的字 符串: - 41 - invoke print 大家可以看出,访问这个url时并没有调用abcd方法。如果随便指定的action值的话,则只调 用abcd方法,如访问http://localhost:8080/struts2/test/ddd.action?action=aaa,就只会输出 invoke abcd。 二、拦截器的参数 我们在使用很多Struts2内置的拦截器时会发现有很多拦截器都带参数,当然。我们自 己做的拦截器也可以加上同样的参数。有两个参数比较常用,这两个参数是includeMethods 和excludeMethods,其中includeMethods指定了拦截器要调用的 Action类的执行方法(默 认是execute),也就是说,只有在 includeMethods中指定的方法才会被 Struts2调用,而 excludeMethods恰恰相反,在这个参数中指定的执行方法不会被Struts2调用。如果有多个 方法,中间用逗号(,)分隔。在Struts2中提供了一个抽象类来处理这两个参数。这个类如下: com.opensymphony.xwork2.interceptor.MethodFilterInterceptor 如有继承于这个类的拦截器类都会自动处理includeMethods和excludeMethods参数,如下 面的拦截器类所示: package interceptor; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.*; public class MyFilterInterceptor extends MethodFilterInterceptor { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override protected String doIntercept(ActionInvocation invocation) throws Exception { System.out.println("doIntercept"); System.out.println(name); return invocation.invoke(); } - 42 - } MethodFilterInterceptor的子类需要实现 doIntercept方法(相当于 Interceptor的intercept方 法),如上面代码所示。在上面的代码中还有一个name属性,是为了读取拦截器的name属性 而设置的,如下面的配置代码所示: abcd 中国 再次 访 问 http://localhost:8080/struts2/test/ddd.action?action=test, Struts2就会 调 用 MyFilterInterceptor的doIntercept方法来输出 name属性值。如果将上面的 includeMethods 参数值中的abcd去掉,则Action类的abcd方法不会被执行。 Struts2Struts2Struts2 Struts2 教程101010 10 :国际化 国际化的作用就是根据不同国家的用户在访问Web或其他类型的程序时,将各种信息以本 地的常用形式显示出来,如界面信息在中国,就会显示中文信息,在以英文为主的国家里, 就会显示英文信息。还有就是一些信息的格式,如日期格式等。 从属性文件中获得字符串信息是国际化的基本应用。在 Struts2中使用的属性文件就是Java - 43 - 属性文件,扩展名为properties。在Struts2中的属性文件可以有很多默认的位置,Struts2可 按如下的顺序(或步骤)来定位属性文件: 1. ActionClass.properties:属性文件名和动作类同名。 Struts2会首先查询与当前访问 的动作类同名,并且和ActionClass.class在同一个目录下的属性文件。 2. BaseClass.properties :BaseClass 表示动作类的基类。所有动作类都会查找 Object.properties文件(因为Object是所有Java类的基类),但要注意的是Object.properties 文件可不能放到当前动作类的目录中,由于Object在java.lang包 中 ,因此,Object.properties 要放到jdk包的java"lang目录中。而对于ActionSupport.properties文件,当然也不能放到动 作类的当前目录中,由于ActionSupport类中com.opensymphony.xwork2名中,因此,需要 将ActionSupport.properties文件放到xwork2.jar包中的com\opensymphony\xwork2目录中, 由于放到 jar文件中不太方便,因此,可以使一个和当前动作类在一个目录的类先继承 ActionSupport,然后所有的动作类都继承于这个类。代码如下: public class MyActionSupport extends ActionSupport { ...... } public class ActionClass extends MyActionSupport { ...... } 这样的话,只要存在一个MyActionSupport.properties,在当前目录下的所有动作类都会读 取这个文件。 3. Interface.properties:这类文件和 BaseClass.properties类似,Interface表示动作类实现 的接口。 4. 如果动作类实现了ModelDriven, 那么重复第1步。 5. package.properties:大家要注意。这个文件就叫 package.properties。不象Interface和 BaseClass都是泛指。这个文件可以放到当前动作类的包的任何一层目录下。如当前动作类 在action.test包中。那么package.properties可以放到action目录中,也可以放到action"test 目录中。Struts2会从离动作类最近的位置开始查找package.properties文件。 6. 搜索il8n资源信息 7. 查找全局资源属性文件 如下面是一个动作类 package action.test; import org.apache.struts2.*; import com.opensymphony.xwork2.ActionSupport; - 44 - public class Internationalizing extends ActionSupport { public String execute() throws Exception { return "forward"; } } 在action\test目录下有一个Internationalizing.properties文件,内容如下: delete = 删除 save = 保存 我们可以在jsp文件中使用如下几种方法取出资源信息: 3. 使用 标签。这个标签可以直接定位属性文件,如abc.properties在WEB- INF\classes\test目录下,内容和Internationalizing.properties一样,则可以使用如下的代码 读取abc.properties的内容: <%@ taglib prefix="s" uri="/struts-tags" %> 当然,我们也可以使用全局的属性文件,在WEB-INF"classes 目录下建立一个 struts.properties文件,内容如下: struts.custom.i18n.resources=my 在WEB-INF\classes 目录下建立一个my.properties文件,当Struts2按着上述的顺序没有找到 相应的属性文件时,最后就会考虑寻找全局的属性文件,因此,就会找到my.properties。 还可以通过属性文件名来让Struts2按着客户端浏览器的语言环境来找符合某种语言的属 性文件。如有三个属性文件language.properties、language_en.properties、language_zh.properties。 如果客户端的语言是中文,Struts2就会读 language_zh.properties,如果是英文 ,就会读 language_en.properties。如果这两个文件的某个不存在,就会读 language.properties。读者可 通过IE的[工具]->[Internet]->[语言]来测试客户端浏览器的语言,如图1所示: - 45 - 图1 2008年7月5日

还剩44页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 20 金币 [ 分享pdf获得金币 ] 0 人已下载

下载pdf

pdf贡献者

heyuqing

贡献于2011-06-15

下载需要 20 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!