chain
用来处理Action链
com.opensymphony.xwork2.ActionChainResult
dispatcher
用来转向页面,通常处理JSP
org.apache.struts2.dispatcher.ServletDispatcherResult
freemaker
处理FreeMarker模板
org.apache.struts2.views.freemarker.FreemarkerResult
httpheader
控制特殊HTTP行为的结果类型
org.apache.struts2.dispatcher.HttpHeaderResult
redirect
重定向到一个URL
org.apache.struts2.dispatcher.ServletRedirectResult
redirectAction
重定向到一个Action
org.apache.struts2.dispatcher.ServletActionRedirectResult
stream
向浏览器发送InputSream对象,通常用来处理文件下载,还可用于返回AJAX数据
org.apache.struts2.dispatcher.StreamResult
velocity
处理Velocity模板
org.apache.struts2.dispatcher.VelocityResult
xslt
处理XML/XLST模板
org.apache.struts2.views.xslt.XSLTResult
plainText
显示原始文件内容,例如文件源代码
org.apache.struts2.dispatcher.PlainTextResult
redirect-action
重定向到一个Action
org.apache.struts2.dispatcher.ServletActionRedirectResult
plaintext
显示原始文件内容,例如文件源代码
org.apache.struts2.dispatcher.PlainTextResult
Struts2将Result列为一个独立的层次,可以说是整个Struts2的Action层架构设计中的另外一个精华所在。Result之所以成为一个层次,其实
是为了解决MVC框架中,如何从Control层转向View层这样一个问题而存在的
在struts2-core.jar/struts-default.xml中,我们可以找到关于result-type的一些配置信息,从中可以看出struts2组件默认为我们提供了这
些result-type
<result-types> <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/> <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/> <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/> <result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/> <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/> <result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/> <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/> <result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/> <result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/> <result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" /> <!-- Deprecated name form scheduled for removal in Struts 2.1.0. The camelCase versions are preferred. See ww-1707 --> <result-type name="redirect-action" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/> <result-type name="plaintext" class="org.apache.struts2.dispatcher.PlainTextResult" /> </result-types>
封装跳转逻辑
Result的首要职责,是封装Action层到View层的跳转逻辑。之前我们已经反复提到,Struts2的Action是一个与Web容器无关的POJO。所以,在Action执行完毕之后,框架需要把代码的执行权重新交还给Web容器,并转向到相应的页面或者其他类型的View层。而这个跳转逻辑,就由Result来完成。这样,好处也是显而易见的,对Action屏蔽任何Web容器的相关信息,使得每个层次更加清晰。
View层的显示类型非常多,有最常见的JSP、当下非常流行的Freemarker/Velocity模板、Redirect到一个新的地址、文本流、图片流、甚至是JSON对象等等。所以Result层的独立存在,就能够对这些显示类型进行区分,并封装合理的跳转逻辑。
以JSP转向为例,在Struts2自带的ServletDispatcherResult中就存在着核心的JSP跳转逻辑:
常用的Result
接下来,大致介绍一下Struts2内部已经实现的Result,并看看他们是如何工作的。
dispatcher
<result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
dispatcher主要用于返回JSP,HTML等以页面为基础View视图,这个也是Struts2默认的Result类型。在使用dispatcher时,唯一需要指定的,是JSP或者HTML页面的位置,这个位置将被用于定位返回的页面:
<result name="success">/index.jsp</result>
而Struts2本身也没有对dispatcher做出什么特殊的处理,只是简单的使用Servlet API进行forward。
freemarker / velocity
<result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/> <result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>
随着模板技术的越来越流行,使用Freemarker或者Velocity模板进行View层展示的开发者越来越多。Struts2同样为模板作为Result做出了支持。由于模板的显示需要模板(Template)与数据(Model)的紧密配合,所以在Struts2中,这两个Result的主要工作是为模板准备数据。
以Freemarker为例,我们来看看它是如何为模板准备数据的:
public void doExecute(String location, ActionInvocation invocation) throws IOException, TemplateException {
this.location = location;
this.invocation = invocation;
this.configuration = getConfiguration();
this.wrapper = getObjectWrapper();
// 获取模板的位置
if (!location.startsWith("/")) {
ActionContext ctx = invocation.getInvocationContext();
HttpServletRequest req = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST);
String base = ResourceUtil.getResourceBase(req);
location = base + "/" + location;
}
// 得到模板
Template template = configuration.getTemplate(location, deduceLocale());
// 为模板准备数据
TemplateModel model = createModel();
// 根据模板和数据进行输出
// Give subclasses a chance to hook into preprocessing
if (preTemplateProcess(template, model)) {
try {
// Process the template
template.process(model, getWriter());
} finally {
// Give subclasses a chance to hook into postprocessing
postTemplateProcess(template, model);
}
}
}
public void doExecute(String location, ActionInvocation invocation) throws IOException, TemplateException {
this.location = location;
this.invocation = invocation;
this.configuration = getConfiguration();
this.wrapper = getObjectWrapper();
// 获取模板的位置
if (!location.startsWith("/")) {
ActionContext ctx = invocation.getInvocationContext();
HttpServletRequest req = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST);
String base = ResourceUtil.getResourceBase(req);
location = base + "/" + location;
}
// 得到模板
Template template = configuration.getTemplate(location, deduceLocale());
// 为模板准备数据
TemplateModel model = createModel();
// 根据模板和数据进行输出
// Give subclasses a chance to hook into preprocessing
if (preTemplateProcess(template, model)) {
try {
// Process the template
template.process(model, getWriter());
} finally {
// Give subclasses a chance to hook into postprocessing
postTemplateProcess(template, model);
}
}
}
从源码中,我们可以看到,createModel()方法真正为模板准备需要显示的数据。而之前,我们已经看到过这个方法的源码,这个方法所准备的数据不仅包含ValueStack中的数据,还包含了被封装过的HttpServletRequest,HttpSession等对象的数据。从而使得模板能够以它特定的语法输出这些数据。 [SPAN]
Velocity的Result也是类似,有兴趣的读者可以顺着思路继续深究源码。
redirect
<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/> <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/> <result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
如果你在Action执行完毕后,希望执行另一个Action,有2种方式可供选择。一种是forward,另外一种是redirect。有关forward和redirect的区别,这里我就不再展开,这应该属于Java程序员的基本知识。在Struts2中,分别对应这两种方式的Result,就是chain和redirect。
先来谈谈redirect,既然是重定向,那么源地址与目标地址之间是2个不同的HttpServletRequest。所以目标地址将无法通过ValueStack等Struts2的特性来获取源Action中的数据。如果你需要对目标地址传递参数,那么需要在目标地址url或者配置文件中指出:
Xml代码
<!-- The redirect-action url generated will be : /genReport/generateReport.jsp?reportType=pie&width=100&height=100 --> <action name="gatherReportInfo" class="..."> <result name="showReportResult" type="redirect"> <param name="location">generateReport.jsp</param> <param name="namespace">/genReport</param> <param name="reportType">pie</param> <param name="width">${width}</param> <param name="height">${height}</param> </result> </action>
同时,Redirect的Result支持在配置文件中,读取并解析源Action中ValueStack的值,并成为参数传递到Redirect的地址中。上面给出的例子中,width和height就是ValueStack中的值。