struts2之ResultType的chain,redirect,dispatcher
1 struts2中Result和ResultType
简单的说,Result是Action执行完后返回的一个字符串,它指示了Action执行完成后,下一个页面在哪里。Result仅仅是个字符串,仅仅是用来指示下一个页面的,那么如何才能够到达下一个页面呢?下一个页面如何能正确地展示结果呢?这就该引出一个新概念——ResultType,所谓ResultType,指的是具体执行Result的类,由它来决定采用哪一种视图技术,将执行结果展现给用户。
很多时候,我们并不去区分Result和ResultType,而是笼统的称为Result。因此,Result除了当作上面讲述的字符串来理解外,还可以把Result当作技术,把Result当作实现MVC模型中的View视图的技术,也就是ResultType来看待。
在Struts2中,可以使用多种视图技术,如jsp、freemarker、velocity、jfreechart 等等,同时,Struts2也支持用户自定义ResultType,来打造自己的视图技术。
1.1 预定义ResultType
在Struts2中,预定义了很多ResultType,其实就是定义了很多展示结果的技术。Struts2把内置的<result-type>都放在struts-default包中。struts-default包就是我们配置的包的父包,这个包定义在struts-2.3.16.3.jar包中的根目录下的文件struts-default.xml中。在这个包中,可以找到相关的<result-type>的定义,<result-types>元素是<package>元素的直接子元素。Struts2预定义如下:
<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" />
<result-type name="postback" class="org.apache.struts2.dispatcher.PostbackResult" />
</result-types>
上面的每一个<result-type>元素都是一种视图技术或跳转方式的封装。其中的name属性是在<result>元素中如何引用这种视图技术或跳转方式,对应着<result>元素的type属性的值
可能有朋友会说,我们在配置<result>元素的时候,没有配置过type属性嘛。没错,你确实没有配置过,原因就在于Struts2里面设置了默认的type,如果你没有配置,默认就是dispatcher。这个dispatcher的技术就相当于在Servlet里面的RequestDispatcher的技术,也就是一个页面跳转的技术。而class属性是这种视图技术或跳转方式的具体实现类,这些实现类都已经是Struts2实现好的,我们只需要引用就可以了。
1.2 Result的配置
1.2.1 配置name属性
name属性是用来跟Action的execute方法返回的字符串相对应的,用来指示Action运行后跳转到的下一个页面,因此name属性的值可以是任意字符串。比如有如下的execute方法:
public String execute() throws Exception {
return "toWelcome";
}
那么,这里返回的toWelcome,在struts.xml里面就有如下的配置来对应:
<action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction">
<result name="toWelcome">/s2impl/welcome.jsp</result>
</action>
如果不设置的话,默认值为success,正好和Action中的SUCCESS这个常量相对应,那样的话,execute方法就应该返回SUCCESS,如下:
public String execute() throws Exception {
return this.SUCCESS;
}
此时在struts.xml里面就有如下的配置来对应:
<action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction">
<result>/s2impl/welcome.jsp</result>
<!-- 或者如下的配置
<result name="success">/s2impl/welcome.jsp</result>
-->
</action>
1.2.2 配置type属性
<result>元素的type属性也可以是任意字符串,不过,一定是某一个<result-type>元素的name属性。在没有自定义ResultType的情况下,type属性的值,就是在struts-default.xml中所定义的<result-type>的name属性的值。比如:
<action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction">
<result name="toWelcome" type="dispatcher">/s2impl/welcome.jsp</result>
</action>
这里的dispatcher就是在struts-default.xml中所定义的默认的<result-type>的name属性值。既然是默认的,那就可以不用配置,也就是说,上面的配置跟如下配置是等价的,示例如下:
<action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction">
<result name="toWelcome">/s2impl/welcome.jsp</result>
</action>
1.2.3 全局Result
全局Result本身没有任何的特异之处,同样是配置name属性和type属性,包括如何指定jsp的位置都和普通的Result一样,只不过其<result>元素并不是<action>元素的子元素,而是作为<global-results>元素的子元素,而<global-results>元素又是<package>元素的子元素,示例如下:
<package name="helloworld" extends="struts-default">
<global-results>
<result name="toLogin">/login.jsp</result>
</global-results>
<action ……>
……
</action>
</package>
1.2.4 搜寻Result的顺序
在有了全局Result之后,需要讨论一下在Action运行之后,根据execute方法的返回值寻找Result顺序了
- 首先,先找自己的
<action>元素内的<result>元素是否有匹配的,如果有就执行这个Result,如果没有,下一步。 - 其次,再找自己的
<action>所在的包的全局Result,看看是否有匹配的,如果有就执行这个Result,如果没有,下一步。 - 再次,递归的寻找自己的包的父包、祖父包中的全局
Result是否有匹配的,如果有就执行这个Result,如果没有,下一步。 - 最后,如果上述三种情况都没有匹配的
Result的话,则抛出Exception。
注意:如果出现同名的Result,上述的顺序也是Result之间的优先顺序。也就是说,如果Action的execute方法返回的字符串,在局部Result和全局Result中都有能匹配的配置,那么以局部Result为准。
2 struts2中ResultType
2.1 dispatcher
2.1.1 dispatcher基本使用
名称为dispatcher的ResultType,在struts-default.xml里的配置如下:
<result-type name="dispatcher"
class="org.apache.struts2.dispatcher.ServletDispatcherResult"
default="true"/>
通过配置可以看出,它对应的实现类是ServletDispatcherResult
如果采用JSP作为视图的实现技术,那么这个ResultType是最常用的。在这个ResultType的实现中,调用了javax.servlet.RequestDispatcher类的forward方法,也就是说它相当于是对RequestDispatcher的一个再包装。
既然是这样,那么在Servlet中使用RequestDispatcher来进行页面跳转的特性,也就自然被dispatcher这个ResultType继承下来了。那么Servlet中的RequestDispatcher,到底有什么特性呢?那就是是通过RequestDispatcher来进行页面跳转,将会保持是同一个请求对象。这有什么好处呢?由于是同一个对象,那就意味着有同样的数据,而请求对象里面数据众多,在Servlet的request对象里面,典型有如下数据:
- 参数区(
parameter),就是用户在页面上填写并提交的数据 Head区,由浏览器在发出请求的时候,自动加入到请求包的数据- 属性区(
Attribute),由开发人员存储在属性区的值,通常是通过request.setAttribute方法、request.getAttribute方法来进行访问 cookie区,由浏览器在发出请求的时候,自动把相关的Cookie数据通过request传递到服务端
2.1.2 dispatcher特殊用法
在<result>元素的定义中可以使用Action的execute方法运行之后的数据。怎么做呢?一起来看看示例。或许我们都已经习惯于以下这种简单的<result>配置:
<result name="toWelcome">/s2impl/welcome.jsp</result>
里面用于指定jsp位置的字符串都是固定的。如果我们希望这个字符串是活动的,可以根据某些参数值来变化,该怎么做到呢?如果我们在Action中定义一个folder字符串,并在execute中或validate中对它进行赋值,这里我们放到validate中,代码如下:
package cn.javass.hello.struts2impl.action;
import com.opensymphony.xwork2.ActionSupport;
public class HelloWorldAction extends ActionSupport {
private String account;
private String password;
private String submitFlag;
private String folder;
public String execute() throws Exception {
this.businessExecute();
return "toWelcome";
}
public void validate(){
this.folder = "s2impl"; //放在这里的原因是:validate先与execute执行,如果fieldError里面有值,execute就不执行了
if(account==null || account.trim().length()==0){
this.addFieldError("account", this.getText("k1"));
}
if(password==null || password.trim().length()==0){
this.addFieldError("password", this.getText("k2"));
}
if(password!=null && !"".equals(password.trim()) && password.trim().length()<6){
this.addFieldError("password", this.getText("k3"));
}
}
/**
* 示例方法,表示可以执行业务逻辑处理的方法,
*/
public void businessExecute(){
System.out.println("用户输入的参数为==="+"account="+account+",password="+password+",submitFlag="+submitFlag);
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSubmitFlag() {
return submitFlag;
}
public void setSubmitFlag(String submitFlag) {
this.submitFlag = submitFlag;
}
public String getFolder() {
return folder;
}
public void setFolder(String folder) {
this.folder = folder;
}
}
那么,在<result>的定义中就可以引用folder这个变量,示例如下:
<package name="helloworld" extends="struts-default">
<action name="helloworldAction" class="cn.javass.hello.struts2impl.action.HelloWorldAction">
<result name="toWelcome" type="dispatcher">/${folder}/welcome.jsp</result>
<result name="input">/${folder}/login.jsp</result>
</action>
</package>
这样配置的结果和前面写死的路径效果时完全一样的。仔细观察一下你会发现,${folder}的写法跟以前在jsp上写的el表达式类似,而里面的folder是跟Action的属性相对应的。
2.1.3 dispatcher更完整的配置方式
平时我们把result对应的jsp的路径,直接作为<result>元素中的文本来配置,这是简化的写法,实际上对于dispatcher还有两个参数可以配置,示例如下:
<result name="toWelcome" type="dispatcher">
<param name="location">/s2impl/welcome.jsp</param>
<param name="parse">true</param>
</result>
location参数就是咱们平时写的下一个jsp的位置,而parse参数决定了location是否可以通过使用OGNL来引用参数,默认为true。其实,前面使用${folder}来引用Action的folder属性的值的例子,就是使用的OGNL来引用参数
2.2 chain
2.2.1 chain基本使用
名称为chain的ResultType,在struts-default.xml里的配置如下:
<result-type name="chain"
class="com.opensymphony.xwork2.ActionChainResult"/>
chain是一种特殊的视图结果,用来将Action 执行完之后链接到另一个Action中继续执行,新的Action使用上一个Action的上下文(ActionContext),数据也会被传递
这在实际开发中,也是经常用到的一种ResultType。比如我们在Servlet开发中,一个请求,被一个Servlet处理过后,不是直接产生相应,而是把这个请求传递到下一个Servlet继续处理,直到需要的多个Servlet处理完成后,才生成响应返回。同样的,在Struts2开发中,也会产生这样的需要,一个请求被一个Action处理过后,不是立即产生响应,而是传递到下一个Action中继续处理。那么这个时候,就需要使用chain这个ResultType了。
来示例一下,先看看第一个Action,就用HelloWorldAction吧,稍微简化一下,示例如下:
public class HelloWorldAction extends ActionSupport {
private String account;
private String password;
private String submitFlag;
public String execute() throws Exception {
this.businessExecute();
return "toSecond";
}
public void businessExecute(){
System.out.println("用户输入的参数为==="+"account="+account+",password="+password+",submitFlag="+submitFlag);
}
//属性对应的getter/setter方法,省略了
}
第二个Action,示例代码如下:
public class SecondAction extends ActionSupport {
public String execute() throws Exception {
System.out.println("现在SecondAction进行处理");
return "toWelcome";
}
}
然后到struts.xml中,配置这两个Action,要注意第一个Action的配置,在配置toSecond这个result的时候,用的就是chain这个ResultType,示例如下:
<package name="helloworld" extends="struts-default">
<action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction">
<result name="toSecond" type="chain">
<param name="actionName">secondAction</param>
</result>
<!--下面这样配置也可以
<result name="toSecond" type="chain">secondAction</result>
-->
</action>
<action name="secondAction" class="cn.javass.action.action.SecondAction">
<result name="toWelcome">/s2impl/welcome.jsp</result>
</action>
</package>
2.2.2 chain备注
chain不能在result配置的时候传递参数,也就是说,不能类似于如下的配置:
<result name="toSecond" type="chain">
<param name="actionName">secondAction?account=5</param>
</result>
这种配置方式是不行的,因为这里要求配置的是要链接的Action的name,不能传递参数,那么,要传递参数怎么办呢?那就需要在Action里面使用ActionContext或者ServletActionContext了
使用chian的方式,后面的Action会和前面的Action共用同一个ActionContext
名称为chain的ResultType在配置的时候,除了前面示例中的actionName外,还有一个参数,名称为namespace,表示被链接的Action所在包的命名空间,如果不设置,默认的即是当前的命名空间。配置示例如下:
<result name="toSecond" type="chain">
<param name="actionName">secondAction</param>
<param name="namespace">其他Package的namespace</param>
</result>
2.3 redirect
2.3.1 redirect基本使用
名称为redirect的ResultType,在struts-default.xml里的配置如下:
<result-type name="redirect"
class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
通过配置可以看出,它对应的实现类是ServletRedirectResult。
这种Result同常也使用JSP作为视图技术。它包装的是javax.servlet.http.HttpServletResponse类的sendRedirect方法,这个ResultType也是用来实现跳转到下一个页面的。但是它的功能与上面的dispatcher不同,redirect的特点是全新的请求,这就意味着,本次请求和跳转到下一个页面的请求是不同的对象,因此它们的值是不一样的。
2.3.2 redirect特殊用法
对比着dispatcher的ResultType,我们看看它的特点。同样在<result>元素的定义中可以使用Action的execute方法运行之后的数据。同样在Action中定义一个folder字符串,并在execute或者validate方法中对它赋值。那么,在<result>的定义中就可以引用folder这个变量,示例如下:
<result name="toWelcome"
type="redirect">/${folder}/welcome.jsp</result>
由于redirect采取重定向的方式,下一个页面会取不到上一个请求对象里面的值,如果要传值的话,可以采用get的方式传参。示例如下:
<result name="toWelcome" type="redirect">
/${folder}/welcome.jsp?account=${account}
</result>
上面这个配置,会向新请求里面传入account的参数,这样在欢迎页面就可以获取到account的值了。但是,前面写的欢迎页面是取不到这个``account的值的,为什么呢?先来看看前面写的欢迎页面取值的那句话,如下:
欢迎账号为<s:property value="account"/>的朋友来访
以前的欢迎页面,是通过使用Struts2的标签来获取的account的值,Struts2的标签会到Struts2的值栈里面去取值,而这里是执行Result的时候,才再请求上添加了account这么一个参数,然后就直接回到页面了,根本不会再走一次Struts2的运行过程,也就是说,这里传递的这个参数,根本不会进入到这个请求对应的值栈,因此这里这个写法是取不到值的。
那么该怎么写才能获取到这个account参数的值呢?有两个简单的方法,一个是直接使用Servlet的HttpServletRequest对象的方法来获取参数,另外一个方法是直接使用EL表达式,示例如下:
欢迎账号为${param.account}的朋友来访
<br/>
欢迎账号为<%=request.getParameter("account") %>的朋友来访
2.3.3 更完整的配置方式
与dispatcher一样,redirect也可以配置<param>,同样可以配置location和parse,如下:
<result name="toWelcome" type="redirect">
<param name="location">/s2impl/welcome.jsp</param>
<param name="parse">true</param>
</result>

浙公网安备 33010602011771号