大多数情况下我们的用例可以被分成两个部分。一是我们需要改变和查询应用程序的状态,而是需要呈现和更新应用程序的视图。在Struts2中Action管理应用程序的状态,Result Type管理视图。
Rsult是什么
简单的说Result就是Action方法执行完毕之后返回的一串字符串,他指示出Action执行完之后的下一个页面在哪里,具体页面的位置是我们在struts.xml中配置的,就是<result>子元素,例如我们在前面UserAction中配置的Result:
<actionname="*User"class="action.UserAction"method="{1}">
<resultname="input">/input.jsp</result>
<resultname="success">success.jsp</result>
</action>
Action执行完之后返回的字符串就是雨上面result元素的name属性的值相对应。
Result Type
对于Result,实际上就是一串字符串而已,它并不能完成什么工作,真正完成执行Result功能的就是Result Type所对应的实现类,通常情况下我们并不区分Result和Result Type。在Struts2中内建了许多ResultType,他们都定义在strtus-default包中,我们可以在struts-default.xml中找到这些Result Type的定义:
<result-types>
<result-typename="chain"class="com.opensymphony.xwork2.ActionChainResult"/>
<result-typename="dispatcher"class="org.apache.struts2.dispatcher.ServletDispatcherResult"default="true"/>
<result-typename="freemarker"class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
<result-typename="httpheader"class="org.apache.struts2.dispatcher.HttpHeaderResult"/>
<result-typename="redirect"class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
<result-typename="redirectAction"class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
<result-typename="stream"class="org.apache.struts2.dispatcher.StreamResult"/>
<result-typename="velocity"class="org.apache.struts2.dispatcher.VelocityResult"/>
<result-typename="xslt"class="org.apache.struts2.views.xslt.XSLTResult"/>
<result-typename="plainText"class="org.apache.struts2.dispatcher.PlainTextResult"/>
</result-types>
具体的Rsult Type的作用在Stuts2的文档中有描述:
虽然Strtus2内建支持这么多Result Type,但是一般情况下我们都用不到这么多,因此我们挑几个常用的学习一下,其他的可以在需要的时候再去查看文档。
Dispatcher
从是struts2.xml的配置中可以看出对于Dispatcher类型的Result Type被设置为默认使用的结果类型。与Dispatcher类型对应的实现类为org.apache.struts2.dispatcher.ServletDispatcherResult,查看该类的源代码,其中的doExecute方法:
public void doExecute(String finalLocation, ActionInvocation invocation) throws Exception { if (LOG.isDebugEnabled()) { LOG.debug("Forwarding to location " + finalLocation); } PageContext pageContext = ServletActionContext.getPageContext(); if (pageContext != null) { pageContext.include(finalLocation); } else { HttpServletRequest request = ServletActionContext.getRequest(); HttpServletResponse response = ServletActionContext.getResponse(); RequestDispatcher dispatcher = request.getRequestDispatcher(finalLocation); //add parameters passed on the location to #parameters // see WW-2120 if (invocation != null && finalLocation != null && finalLocation.length() > 0 && finalLocation.indexOf("?") > 0) { String queryString = finalLocation.substring(finalLocation.indexOf("?") + 1); Map parameters = (Map) invocation.getInvocationContext().getContextMap().get("parameters"); Map queryParams = UrlHelper.parseQueryString(queryString, true); if (queryParams != null && !queryParams.isEmpty()) parameters.putAll(queryParams); } // if the view doesn't exist, let's do a 404 if (dispatcher == null) { response.sendError(404, "result '" + finalLocation + "' not found"); return; } //if we are inside an action tag, we always need to do an include Boolean insideActionTag = (Boolean) ObjectUtils.defaultIfNull(request.getAttribute(StrutsStatics.STRUTS_ACTION_TAG_INVOCATION), Boolean.FALSE); // If we're included, then include the view // Otherwise do forward // This allow the page to, for example, set content type if (!insideActionTag && !response.isCommitted() && (request.getAttribute("javax.servlet.include.servlet_path") == null)) { request.setAttribute("struts.view_uri", finalLocation); request.setAttribute("struts.request_uri", request.getRequestURI()); dispatcher.forward(request, response); } else { dispatcher.include(request, response); } } }
可以看出Dispatcher内部使用了Servlet API,主要是使用RequestDispatcher类进行了请求的转交。转发有两种方式,一是使用include方式,一是forward方式,这两种方式有些不同。使用include方式是临时转交,在转发之前前一个Action已经开始写入了响应,include进来的内容只能作为请求的一部分。使用forward方法进行的转交是永久转交,在转交之前前一个Action并没有开始写入响应。但是他们都与重定向的方式有所不同,整个转交过程都属于同一个Request请求。
Redirect
redirect类型的Result Type使用了HttpServletResponse的sendRedirect方法来完成重定向。使用冲定向的方式将不会保留前一个Action的状态,他们不再是同一个Request请求。如果要想在重定向之后能够访问前一个Action的状态,那么我们就需要将需要的信息保存在session中或者在result的url中通过查询参数的方式进行传递。
<result name="toWelcome" type="redirect">/${folder}/welcome.jsp?account=${account}</result>
在strtus.xml中配置result的时候如果要动态的访问Action中的属性值,那么动态值需要放在“${}”内才能被正确解析,另外就是一些特殊字符如”&”和”+”都需要使用相应的转义序列来表示。
对于dispacher类型和redirect类型的result的配置,我们都是用了简便的方式,实际上他们有两个共同的参数:
location:用来指出结果视图的地址
parse:用来表明是否要把location参数的值当作OGNL表达式来解析。默认值是true。
1. <result name="toWelcome" type="dispatcher">
2. <param name="location">/s2impl/welcome.jsp</param>
3. <param name="parse">true</param>
4. </result>
Redirect Action
这种结果类型与Redirect结果类型的行为有几分相似,但是Redirect Action不是重定向到另一个资源,而是重定向到另一个动作。它可以有一下几个参数:
actionName:用来指定目标Action的名字,是默认属性,可以不用显示指出。
namespace:用于指定目标Action的命名空间,如果没有指定值,那么Struts2会认为目标Action与起始Action的命名空间相同。
<actionname="userAction"class="action.UserAction">
<resultname="next"type="redirect-action">
<paramname="actionName">userInput</param>
<paramname="namespace">hello</param>
</result>
</action>
除了这两个参数之外,还可以使用相同的形式来想目标Action传递参数。
<resultname="next"type="redirect-action">
<paramname="actionName">userInput</param>
<paramname="userName">hello</param>
<paramname="password">world</param>
</result>
目标result地址将被翻译成:
userInput.action?userName=hello&passworld=world
Strtus2的除了支持Jsp作为结果页面之外还内建支持常用的两个模版引擎FreeMaker和Velocity。他们对应的Result Type是freenaker和velocity。有关这两种模版引擎技术的使用和介绍可以在需要的时候查看相关文档。
全局Result
以前我们的result都配置在action元素里面,这种result叫做局部result。他们只能被特定的action所访问。有时候我们需要将一些result作为多个action共享的资源,那么我们就可以将这些result设置为全局Result。他们的配置不再放在action元素中,而是放在package元素中了:
<packagename="default"namespace="/"extends="struts-default">
<global-results>
<resultname="error">/error.jsp</result>
<resultname="login">/admin/login.jsp</result>
</global-results>
<actionname="*User"class="action.UserAction"method="{1}">
<resultname="input">/input.jsp</result>
<resultname="success">success.jsp</result>
</action>
</package>
当我们配置了这些result之后,需要大致了解一下这些result的查找循序:
(1) 首先会在<action>元素内查找是否有匹配的<result>元素,如果有则执行这个result,如果没有则执行下一步
(2) 其次,在查找自己所在的package中的全局result,看有没有匹配的,如果有则执行,没有则执行下一步
(3) 再次,递归查找自己所在包的父包中的全局result,如有匹配的result则执行,如果没有则执行下一步
(4) 最后,以上三种情况均没有找到匹配的result,那么struts2就会抛出异常。
注:这个顺序同样适用有多个同名result时的执行优先级。
在学习Action的时候学到了Action的匹配可以使用通配符,同样对于Result的匹配也可以使用通配符,原理和用法相同:
1. <action name="*_*_*_*" class="cn.javass.action.action.{1}Action" method="{2}">
2. <result name="{3}">/${folder}/{
4}.jsp</result>3. </action>