struts2之ResultType的chain,redirect,dispatcher

1 struts2中Result和ResultType

简单的说,ResultAction执行完后返回的一个字符串,它指示了Action执行完成后,下一个页面在哪里。Result仅仅是个字符串,仅仅是用来指示下一个页面的,那么如何才能够到达下一个页面呢?下一个页面如何能正确地展示结果呢?这就该引出一个新概念——ResultType,所谓ResultType,指的是具体执行Result的类,由它来决定采用哪一种视图技术,将执行结果展现给用户。

很多时候,我们并不去区分ResultResultType,而是笼统的称为Result。因此,Result除了当作上面讲述的字符串来理解外,还可以把Result当作技术,把Result当作实现MVC模型中的View视图的技术,也就是ResultType来看待。
Struts2中,可以使用多种视图技术,如jspfreemarkervelocityjfreechart 等等,同时,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属性是用来跟Actionexecute方法返回的字符串相对应的,用来指示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之间的优先顺序。也就是说,如果Actionexecute方法返回的字符串,在局部Result和全局Result中都有能匹配的配置,那么以局部Result为准。

2 struts2中ResultType

2.1 dispatcher

2.1.1 dispatcher基本使用

名称为dispatcherResultType,在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来进行页面跳转,将会保持是同一个请求对象。这有什么好处呢?由于是同一个对象,那就意味着有同样的数据,而请求对象里面数据众多,在Servletrequest对象里面,典型有如下数据:

  • 参数区(parameter),就是用户在页面上填写并提交的数据
  • Head区,由浏览器在发出请求的时候,自动加入到请求包的数据
  • 属性区(Attribute),由开发人员存储在属性区的值,通常是通过request.setAttribute方法、request.getAttribute方法来进行访问
  • cookie区,由浏览器在发出请求的时候,自动把相关的Cookie数据通过request传递到服务端 

2.1.2 dispatcher特殊用法

<result>元素的定义中可以使用Actionexecute方法运行之后的数据。怎么做呢?一起来看看示例。或许我们都已经习惯于以下这种简单的<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}来引用Actionfolder属性的值的例子,就是使用的OGNL来引用参数

2.2 chain

2.2.1 chain基本使用

名称为chainResultType,在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>  

这种配置方式是不行的,因为这里要求配置的是要链接的Actionname,不能传递参数,那么,要传递参数怎么办呢?那就需要在Action里面使用ActionContext或者ServletActionContext
使用chian的方式,后面的Action会和前面的Action共用同一个ActionContext
名称为chainResultType在配置的时候,除了前面示例中的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基本使用

名称为redirectResultType,在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特殊用法

对比着dispatcherResultType,我们看看它的特点。同样在<result>元素的定义中可以使用Actionexecute方法运行之后的数据。同样在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参数的值呢?有两个简单的方法,一个是直接使用ServletHttpServletRequest对象的方法来获取参数,另外一个方法是直接使用EL表达式,示例如下:

欢迎账号为${param.account}的朋友来访  
<br/>
欢迎账号为<%=request.getParameter("account") %>的朋友来访  

2.3.3 更完整的配置方式

dispatcher一样,redirect也可以配置<param>,同样可以配置locationparse,如下:

<result name="toWelcome" type="redirect">  
    <param name="location">/s2impl/welcome.jsp</param>  
    <param name="parse">true</param>  
</result>
posted @ 2021-09-25 20:08  上善若泪  阅读(53)  评论(0编辑  收藏  举报