return result;
5.2 常用的 Result
我们首先了解一下 Struts2 是在什么地方定义以及配置 Result 返回类型的。将下载的 Struts2.0.9.zip 压缩包解压之后,在路径\struts-2.0.9-all\struts-2.0.9\src\core\src\main\resources 下有一个 struts-default.xml 文件,所有的 Result 类型都是在这里进行配置,在这里能看到对 应 Result 类型的源代码定义在什么地方。下面是这个文件中关于 Result 类型配置部分的代 camelCase versions are preferred. See ww-1707 -->
<result-type name="redirect-action"
表 5-1 Result 类型列表
Result 类型 Result 对应的功能
Dispatcher Result 用于 JSP 的整合
Redirect Result 用于直接跳转到例外的 URL
Chain Result 用于 Action Chaining
XSL Result 用于 XML/XSLT 整合
HttpHeader Result 用于控制特殊的 HTTP 行为
Stream Result 用于向浏览器返回一个 InputStream (一般用于文件下载)
PlainText Result 用于显示某个页面的原始的文本 (例如 jsp, html 等)
Redirect Action Result 用于直接跳转到另外的 action
Velocity Result 用于 Velocity 的整合
FreeMarker Result 用于 FreeMarker 的整合 JasperReports Result 用于 JasperReports 的整合
下面就开始进入探讨 Result 类型之旅了。我们会介绍其中的三种常用 Result 类型以及 其他三种关于视图的 Result 类型。
5.2.1 Dispatcher
要想真正理解 Dispatcher 的作用,应该首先通过 struts-default.xml 找到 Dispatcher 对应 的类文件,查看其源代码。
在目录\Struts2-.0.9\Struts2-src-.0.9\com\opensymphony\Struts2\dispatcher 下可以找到一个 ServletDispatcherResult.java 文件。下面是截取 ServletDispatcherResult.java 的部分代码:
ServletDispatcherResult.java /*省略语句*/
public class ServletDispatcherResult extends StrutsResultSupport { /*省略语句*/
private static final Log log = LogFactory.getLog(ServletDispatcherResult.class);
/*省略部分方法*/
//实现从父类继承的方法
public void doExecute(String finalLocation, ActionInvocation invocation) throws Exception { if (log.isDebugEnabled()) {
if(!response.isCommitted()&&(request.getAttribute("javax.servlet.include.servlet_path
在前面自定义 Result 的时候说过,任何 Result 类型都需要实现 Result 接口的 execute() 方 法 , 但 是 在 此 处 并 未 实 现 , 为 什 么 呢 ? 在 上 述 代 码 中 的 粗 体 部 分 可 以 看 到 ServletDispatcherResult 类是继承 StrutsResultSupport 类的,那再分析一下 StrutsResultSupport 这个类,在与 ServletDispatcherResult.java 文件相同目录下可以找到 StrutsResultSupport.java 文件,其部分代码如下:
StrutsResultSupport.java
/*省略语句*/ //这个类实现了 Result 接口
public abstract class Struts2ResultSupport implements Result, StrutsStatics { private static final Log _log = LogFactory.getLog(Struts2ResultSupport.class);
public static final String DEFAULT_PARAM = "location";
private boolean parse;
private boolean encode;
private String location;
private String lastFinalLocation;
/*省略一些方法*/
public void setLocation(String location) { this.location = location;
}
public String getLastFinalLocation() { return lastFinalLocation;
public void execute(ActionInvocation invocation) throws Exception { lastFinalLocation = conditionalParse(location, invocation);
doExecute(conditionalParse(location, invocation), invocation);
}
protected String conditionalParse(String param, ActionInvocation invocation) { /*省略语句*/
}
protected abstract void doExecute(String finalLocation, ActionInvocation invocation) throws Exception;
}
看了 StrutsResultSupport.java 文件,想必大家就很明白为什么 ServletDispatcherResult 类 没有实现 Result 接口的 execute()方法了。文中粗体所示的部分表明 StrutsResultSupport 类是 一个抽象类,在它里面实现了 execute()方法,不过它的实现是调用一个 doExecute()方法。
在继承 StrutsResultSupport 类的时候,只要实现 doExecute()方法就能达到对 Result 接口 execute()方法的实现!所以在 ServletDispatcherResult.java 文件中只有这一个方法。
下面来看看 Result 的参数,就是在配置 struts.xml 文件时<result>标签对应的参数。在 Struts2ResultSupport.java 文件中用粗体表示的还有一些代码:
public static final String DEFAULT_PARAM = "location";
protected boolean parse ; protected String location;
这三句代码就定义了 Dispatcher 的两个参数,各自功能如下:
location (默认):执行后转到的地方(如 jsp 页面)。
parse:这个值在构造函数中已经默认为 true。如果设置为 false,location 参数就不 会被解析为 Ognl 表达式。
location 对应的就是自己编写的页面的地址,如果要返回一个页面就得拥有这个参数,
否则定义的返回类型就不能返回到指定的页面。在以后介绍的每一种 Result 类型的时候,
都会有这个 DEFAULT_PARAM 静态字符串变量。这个变量指明 location 是默认的参数,这 就让使用者能够更方便的使用这种类型。
清楚了返回类型怎么实现之后,接下来要考虑如何如在 Action 中用到它们。首先回顾 一下第一个 Action 的例子,在 xwork.xml 配置文件里有这些代码:
/*省略语句*/
<include file="struts-default.xml" />
/*省略语句*/
<result name="success">/welcome.jsp</result>
/*省略语句*/
在 struts.xml 中将 struts-default.xml 文件包含进来,就是为了可以使用在 struts-default.xml 中定义的各种 Result 类型。再看<result>标签,它只有一个 name 属性,没有 type 属性的指 定也没有参数的指定,为什么可以这样?它怎么知道选择何种 Result 类型?原来在 struts-default.xml 中是这样定义 Dispatcher 的:
<result-type name="dispatcher"
class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
原来已经定义 default 为 true 了,而且其他的 Result 都没有这个定义。location 参数,因 为有了默认的 DEFAULT_PARAM 静态字符串来表示,所以 location 参数也可以不需要显式 表明。那么对于上面的 Result 标签更完整的配置应该是这样的
<result name="success" type="dispatcher">
<param name="location">/welcome.jsp</param>
</result>
至于 parse 参数,它是一个布尔型的,默认值为 true,它是用来解析参数 location 的,
具体的 Ognl 部分请参看以后的关于表达式语言的章节。
弄清楚上述问题之后,现在重点来看 ServletDispatcherResult.java 中的 doExecute()方法,
弄清楚它的工作流程。
ServletDispatcherResult.java /*省略语句*/
if (log.isDebugEnabled()) {
log.debug("Forwarding to location " + finalLocation);
if(!response.isCommitted()&&(request.getAttribute("javax.servlet.include.servlet_path") == null)) { //设置 request 中元素的值
上面是 doExecute()方法的具体实现,可以从粗体部分看出这个 Result 有三种执行方式:
如果在一个 JSP 的范围内(PageContext 对象可用),PageContext 的 include(String)方 法会被调用。
如果没有 PageContext 对象,并且也不在任何形式的 include 中(在 request 的属性中 没有"javax.servlet.include.servlet_path"),那么调用 RequestDispatcher 的 forward 方 法。
否则调用 RequestDispatcher 的 include 方法。
还可以看到,如果 dispatcher==null 的话,会返回 404 代码错误。所以以后在使用 Dispatcher 的时候遇到 404 错误的时候,就应该知道是返回对应的页面地址或者 Action 找不 到的问题。
至于具体的类的含义以及方法是干什么的,读者可以自己去查阅 servlet 的 API 文档。
这就是整个 Dispatcher Result 的流程了。下面用一个流程图来概括一下,如图 5-4 所示。
图5-4 Dispatcher Result的流程
这一节因为是第一次讲解 Struts2 定义的 Result 类型,所以内容比较多。这个小节对源 代码进行了探讨,介绍了参数,执行方式等内容,让读者完全理解 Dispatcher Result。后面 因为有了这节的基础,Result 的讲解将比较简单了。
5.2.2 Redirect
Redirect,即重定向,Action 如果配置这种返回类型,那么就可以有三种不同的返回效 果,分别是返回到页面、连接到另一个 Action、还可以连接到一个网址。有了前面 Dispatcher Result 的基础,Redirect Result 理解起来就比较容易了。让我们看看 Redirect Result 的源代码 是如何实现的。首先看 Redirect 在 struts-default.xml 中的配置:
<result-type
name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
这就是 Redirect 在 struts-default.xml 中的配置,与 Dispatcher 的不同就是没有 default 属性,
通 过 上 一 小 节 可 以 知 道 , Redirect 并 不 是 默 认 的 , 使 用 的 时 候 必 须 指 明 类 型 属 性 type=”redirect”。在 ServletDispatcherResult.java 文件路径下可以找到 ServletRedirectResult.java 文件,其代码如下:
ServletRedirectResult.java /*省略语句*/
public class ServletRedirectResult extends StrutsResultSupport {
private static final Log log = LogFactory.getLog(ServletRedirectResult.class);
/*省略部分代码*/
protected boolean prependServletContext = true;
public void setPrependServletContext(boolean prependServletContext) {
this.prependServletContext = prependServletContext;
}
protected void doExecute(String finalLocation, ActionInvocation invocation) throws Exception { /*省略语句*/
parse:这个值在构造函数中已经默认为 true。如果设置为 false,location 参数不会 被当作 Ognl 表达式解析。
参数和 Dispatcher 是一样的,不过这里的 location 可以是一般的 JSP 页面,可以是一个 Action,还可以是一个其他的网址(譬如:http://www.njupt.edu.cn)。这就是 Redirect 被称为 重定向的原因,可以定向到不同的方式。
下面通过一个简单的例子来了解一下 Redirect。在一个项目里面定义三个 Action,他们 的 Result 的类型都是 redirect,但是他们重定向的对象不同,一个为一般的 JSP 页面,一个 为 Action,一个是一个网址 URL。可以把本书一开始创建的第一个 Action 项目拿出来使用,
在同一个包中再添加两个 Action,这两个 Action 只返回 SUCCESS,不做其他事情。下面是 三个 Action 的对应源代码:
helloworld.java package example;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class helloworld extends ActionSupport {
public String message;
public String name;
public String execute() {
if ( name == null || "".equals(name)||"w".equals(name)) {
message="Blank names or 'w' not allowed";
return INPUT;
}
message = "hello "+name+"!\n";
return SUCCESS;
}
public String getMessage() {
return message;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
} }
这就是第二章实现的第一个 Action helloworld。下面是添加的两个 Action:
OtherTest.java package example;
import com.opensymphony.xwork2.ActionSupport;
public class OtherTest extends ActionSupport {
public String execute() {
public class Test extends ActionSupport {
public String execute() {
return SUCCESS;
} }
这两个 Action 的 execute()方法只是返回 SUCCESS,通过这三个 Action 来试验一下 Redirect 这种返回类型的三种不同返回形式。
最后再看 struts.xml 的配置代码:
struts.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.devMode" value="true" />
<package name="default" extends="struts-default">
<action name="helloworld" class="example.helloworld">
<interceptor-ref name="completeStack" />
<!--返回到自定义的页面-->
<result name="success" type="redirect">hello.jsp</result>
<result name="input">name.jsp</result>
</action>
<!--连接到其他 Action-->
<action name="OtherTest" class="example.OtherTest">
<result name="success" type="redirect">helloworld.action</result>
</action>
<action name="Test" class="example.Test">
<!--连接到到一个网址-->
<result name="success" type="redirect">http://www.njupt.edu.cn</result>
</action>
</package>
</struts>
最后再看一下 name.jsp 这个页面文件的代码:
name.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head> <title>Enter your name</title> </head>
<body>
<s:if test="message != null">
<font color="red">
<s:property value="message"/>
</font>
</s:if>
Please enter your name:
<s:form action = "helloworld.action">
< s:textfield name ="name"/>
<s:submit/>
</s:form>
</body>
</html>
粗体部分就是与第一个 Struts2 应用中代码不同的部分,提交到 Action 不同,至于 hello.jsp 页面代码就没有变化了。
三个 Action 分别对应了三种不同返回方式,这样很明显的体现出 Redirect 不同之处。
项目中的 web.xml 就不用修改。最后生成的目录结构如图 5-5 所示。
图5-5 完整目录
项目运行后,在浏览器中输入:http://localhost:8080/ww/name.jsp,会出现图 5-6所示的 界面。
图5-6 输入页面
在文本框输入“w”后,点击提交,会出现如下图 5-7 所示的页面。
图5-7 出错处理页面 再输入“world”后,再提交,如图 5-8 所示:
图5-8 RedirectResult 成功的界面
下面是原来项目的成功返回页面,即返回类型为 Dispatcher 时的成功页面,如图 5-9:
图5-9 DispatcheResult成功页面
在浏览器中输入http://localhost:8080/ww/OtherTest.action,链接之后,也会出现如图 5-9 所示的页面,表明它已经转向 helloworld.action 去执行了。
最后再在浏览器中输入http://localhost:8080/ww/Test.action,结果会如图 5-10 所示。
图5-10 南邮主页
从图 5-10 可以看出,浏览器直接进入了南京邮电大学的主页。
从上面的执行流程可以看出,Redirect 实现了上述提到的三种不同的返回方式,所以 Redirect 的功能要比 Dispatcher 的强大。但 Redirect 有自身的不足之处,通过下面与 Dispatche 的比较就会发现。现在看图 5-8 和 5-9 的不同之处,为什么图 5-8 不显示“hello world!”?
这是因为两个 result 的返回类型不相同,工作机制也不相同。Redirect 把一个 HTTP 返回码
(譬如 SUCCESS)以及返回的页面位置一起重新发给 Web 服务器,然后由 Web 服务器产 生一个新的 HTTP 请求,就会产生一个新的线程,保存在原来 Action 执行的线程中的数据,
(譬如 SUCCESS)以及返回的页面位置一起重新发给 Web 服务器,然后由 Web 服务器产 生一个新的 HTTP 请求,就会产生一个新的线程,保存在原来 Action 执行的线程中的数据,