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-defaul
t包中。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>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了