上一篇文章说了Action只是一个逻辑控制器,并不会对用户的请求生成任何的反应,所以要将视图返回给用户,需要<result>标签的帮助。这篇文章就专门讲<result>标签。


1.result的处理流程

用户发出请求后,Struts2框架会寻找相应的Action去处理用户的请求,Action处理完请求后,返回的是一个字符串,整个字符串就是一个逻辑视图名。这时我们通过<result>标签指定逻辑视图和物理视图的对应关系,通常物理视图是一个JSP页面。当Struts2框架接收到Action返回的逻辑视图名后,就去找相应的物理视图,然后呈现给用户。

Struts2框架的result处理流程时序图如下:
(五)struts2进阶之result标签_动态result

文字描述如下:
(1)用户发送请求。
(2)Struts2框架将用户的请求转发给对应的Action。
(3)Action处理完请求后,返回一个逻辑视图(一个普通的字符串)。
(4)Struts2收到逻辑视图后,将请求转发给对应的视图资源。
(5)视图资源将处理结果呈现给用户。

注意:Struts2框架将处理结果转向实际资源时,该实际资源不仅可以是JSP,也可以是FreeMaker视图资源,甚至是转向下一个Action处理,形成Action链式处理。


2.配置result

配置result相信在前面几篇文章中看到好多了,可是一直没仔细说。

其实根据result标签的位置,可以将result分为局部result和全局result。

  • 局部result—–>将标签作为<action>标签的子元素。
  • 全局result—–>将标签作为<global-results>标签的子元素。

<result>标签有name和type两个属性,这两个属性都是可以省略的,name的默认值为success,type的默认值为dispatcher,表示请求转发到JSP页面。(type后面会讲)

(1)局部result

这个不多说了,前面看的多了,也很容易理解。


(2)全局result(必须配置在所有action的前面)

全局result是在<global-results>标签中指定,全局result对所有的Action都有效。

比如我将上篇文章中的struts.xml换成如下

<struts>
    <package name="default" namespace="/" extends="struts-default"                          
        <global-results>
            <result name="edit">/edit.jsp</result>
            <result name="del">/del.jsp</result>
        </global-results>

        <action name="*_*" class="com.codeliu.action.{1}Action" method="{2}">
            <result>/product.jsp</result>
        </action> 
    </package>
</struts>

请求的URL还是不变,则输出结果还是一样。

原因如下:

当Action处理完用户的请求后,首先会去搜索当前Action的局部result,如果没有,就搜索全局result。所以如果有全局result的name和局部result的name相同,则局部result会覆盖全局result。


3.result的type属性

针对不同的视图,要使用不同的type。结果类型如下表所示

结果类型 描述
chain结果类型 用于进行Action链式处理
chart结果类型 用于整合JFreeChart技术
dispatcher结果类型 用于整合JSP技术(默认的)
freemarker结果类型 用于整合FreeMarker技术
httpheader结果类型 用于控制特殊的http行为
jasper结果类型 用于整合jasperReport报表技术
jsf结果类型 用于整合JSF技术
redirect结果类型 用于重定向到其他URL
redirectAction结果类型 用于重定向到其他的Action
stream结果类型 用于向浏览器返回InputStream,一般用于文件下载
tiles结果类型 用于整合Tiles技术
velocity结果类型 用于整合Velocity技术
xslt结果类型 用于整合XML/XSLT技术
plainText结果类型 用于显示某个页面的源代码

其实我们常用到的就是dispatcher、redirect和redirectAction。dispatcher是type属性的默认值,所以我们就讲讲其他两个

(1)redirect类型

redirect类型和dispatcher类型相似,两者最大的区别就是redirect类型是将请求redirect(重定向)到指定的视图资源,而dispatcher类型是将请求forward(转发)到指定的JSP资源。

而转发和重定向的区别就是:重定向会丢失掉所有的请求参数、请求属性,同时Action的处理结果也会丢掉。当使用redirect类型的时候,系统实际上是调用HttpServletResponse的sendRedirect()方法来重定向指定视图资源,这种重定向的效果就是产生一个新的请求,因此请求对象中所有的参数、属性、Action对象和Action中封装的属性全部丢失。

比如我把上面的struts.xml改成下面这样的

<struts>
    <package name="default" namespace="/" extends="struts-default"                          
        <global-results>
            <result name="edit">/edit.jsp</result>
        </global-results>

        <action name="*_*" class="com.codeliu.action.{1}Action" method="{2}">
             <result name="del" type="redirect">/del.jsp</result>
        </action> 
    </package>
</struts>

本来按照上篇文章,del.jsp代码如下

<body>
    ${param.productId}删除成功
</body>

点删除按钮会跳转到del.jsp,并提示哪一号商品删除成功,但如果我们把type的值设为redirect,则运行后,只会输出删除成功,因为Action处理请求后的全部信息已经丢失了。

(2)redirectAction类型

redirectAction类型和redirect类型类似,都是请求重定向。区别在于:redirect类型使用HttpServletResponse的sendRedirect()方法来重定向,而redirectAction类型使用ActionMapperFactory提供的ActionMapper来重定向。

当需要让一个Action处理结束后,直接将请求重定向到另一个Action时,可以使用redirectAction类型。

配置redirectAction类型需要指定actionName和namespace两个参数:

  • actionName参数指定重定向的Action名称
  • namespace参数指定需要重定向的Action所在的命名空间

当两个Action位于同一个package的情况下

<package name="del" namespace="/user" extends="struts-default">
    <action name="delproduct" class="com.codeliu.action.ProductAction" method="del">
        <result name="del">/del.jsp</result>
    </action> 

    <action name="editproduct" class="com.codeliu.action.ProductAction">
        <result type="redirectAction">
            <param name="actionName">delproduct</param>
            <param name="namespace"></param>
        </result>
    </action>
</package>

actionName和namespace这两个参数的配置情况如图,此时param标签的namespace的值为空。

当两个Action位于不同的package的情况下

<struts>
    <package name="del" namespace="/user" extends="struts-default">
        <action name="delproduct" class="com.codeliu.action.ProductAction" method="del">
            <result name="del">/del.jsp</result>
        </action> 
    </package>

    <package name="edit" namespace="/" extends="struts-default">
        <action name="editproduct" class="com.codeliu.action.ProductAction">
            <result type="redirectAction">
                <param name="actionName">delproduct</param>
                <param name="namespace">/user</param>
            </result>
        </action>
    </package>
</struts>

如上所示,此时namespace的值得跟着变化。还是上篇文章的product.jsp,当你点击编辑的时候,使用name属性值为editproduct的Action,处理完结果之后,不会显示edit.jsp,而是重定向到name属性值为delproduct的Action,这时第一个Action的所有信息已经全部丢失掉了,所以传到第二个Action的时候,商品的id已经没有了。所以最好直会到del.jsp,页面显示删除成功。

注意package的namespace不是根命名空间,所以在请求URL的时候记得加上namespace的属性值。


4.动态result

前一篇文章我们讲了动态方法调用,使用了通配符在action标签中,其实通配符同样可以使用在result标签中。

如下代码

<struts>
    <package name="default" namespace="/user" extends="struts-default">
        <action name="*product" class="com.codeliu.action.ProductAction" method="{1}">
            <result name="{1}">/{1}.jsp</result>
        </action> 
    </package>
</struts>

看到没有,又简洁了许多,连视图资源也可以使用{N}进行匹配。