Struts2里的Result

狂风中的少年 提交于 2019-12-18 10:52:28

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

Result

一、Result基础

Result是什么和能干什么?
  1. 简单的说,Result是当Action处理完数据后返回的一个字符串,它指定了下一个页面的位置。比如:
<action name="action_1" class="struts2.com.test.ActionSupport_1">
            <!--suppress Struts2ModelInspection -->
            <result name="success">hellowordactionsuccess.jsp</result>
            <!--suppress Struts2ModelInspection -->
            <result name="input">hellowordactionerror_1.jsp</result>
</action>

action返回的字符串对应的就是result标签里的name属性值。

Result有什么
对于Result
  1. 在struts2中,预定义了以下一些Result的字符常量:
    • SUCCESS:表示Action执行成功,显示结果视图给用户,值为字符串“success”。
    • NONE:表示Action执行成功,不需要显示视图给用户,值为字符串“none”。
    • ERROR:表示Action执行失败,显示错误页面给用户,值为字符串“error”。
    • INPUT:表示Action需要更多的输入信息,回到input对应的页面,值为“input”。
    • LOGIN:表示因用户没有登陆而没有正确执行,将返回该视图,值为字符串“login”。

我们也可以自己定义字符常量,只要能和Action里面的execute里面的返回值对应。

对于ResultType
  1. 在Struts2种ResultType也分成了预定义和自定义两种情况,下面我们讲解。

二、预定义的Result

预定义的ResultType
  1. 在Struts2定义的包中有一个struts-default.xml文件,里面有相关的<result-type>的定义,<result-types>元素是<package>元素的直接子元素。
<result-types>
  <result-type name="dispatcher" class="org.apache.Struts2.dispatcher.ServletDispatcherResult" default="true"/>
  ......
</result-types>
  1. 上面的每一个<result-type>元素都是一种视图技术或跳转方式的封装。其中name的属性是在<result>元素中如何引用这种视图技术或跳转方式,对应着<result>元素的type属性的值。
  2. 在<result>元素中,我们常常是没有书写type的,其实这里我们是使用的默认type,也就是上边的这一段代码,默认的就是dispatcher,相当于Servlet技术里面的RequestDispatcher的技术,也就是服务器跳转技术。
如何配置使用
  1. 在前面我们已经看过很多了在Action中的讲解,Action类返回的字符串对应的就是<resulet>元素里name属性的值。只要完成对应,就会根据type的跳转方式来跳转页面。
  2. 配置type属性可以是任意字符串,不过一定是某一个<result-type>元素的name属性。我们可以使用其中Struts2定义好的,也可以使用自定义的。
dispatcher的ResultType
基本使用
<result-types>
  <result-type name="dispatcher" class="org.apache.Struts2.dispatcher.ServletDispatcherResult" default="true"/>
  ......
</result-types>
  1. 如果使用jsp作为视图技术,那么这个ResultType是最常用的。在这个ResultType的实现中,调用了javax.servlet.RequestDispatcher类的forward方法,也就是说它相当于RequsetDispatcher的一个封装。
  2. 我们在使用中这个type="dispatcher"是可以不用书写的,name也有默认值"success":
  <result name="success" type="dispatcher">getcollection.jsp</result>
几个小的知识点
  1. 对于dispatcher的使用范围,除了可以配置jsp外,还可以配置其他的web资源,比如servlet等,我们只需要在web.xml文件中映射一下<result>中要跳转的页面。比如:
    <servlet>
        <servlet-name>login</servlet-name>
        <servlet-class>struts2.com.test.ModelDriven_1</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>login</servlet-name>
        <url-pattern>/login</url-pattern>
    </servlet-mapping>

在struts.xml中可以配置:

<result name="success">/login</result>

但是请注意,如果这里访问的是一个Action的资源,那么在web.xml文件里就会报错,我们需要使用另一种type名称为”chain“的ResultType。 2. 使用dispatcher的ResultType,不能访问其他web应用中的web资源,因为属于服务器跳转,所以无法跨域跳转,由servlet的forward方法决定。 3. dispatcher有两个参数也是可以配置的:

  • location:默认参数,定位下一个jsp页面的。
  • parse:决定了location是否可以通过使用OGNL来引用参数,默认为true。
<result>
<param name="location">login.jsp</param>
<param name="parse">true</param>
</result>
redirect的ResultType
基本使用
  1. 名称为redirect的ResultType,在struts-default.xml里的配置如下:
  <result-type name="redirect" class="org.apache.struts2.result.ServletRedirectResult"/>

这个ResultType包装的是javax.servlet.http.HttpServletResponse类的sendRedirect方法,属于客户端跳转。 2. 与dispatcher不同的是,他属于全新的请求,这就是说这一次请求就变为新的了,本次请求和跳转到下一个页面的请求是两个请求对象,所以跳转页面之后请求的数据会不存在。这里我们拿redirect和dispatcher来做一下对比。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>dispatcher和redirect</title>
</head>
<body>
<form action="result_1">
    姓名:<input type="text" name="name">
    <input type="submit" value="提交">
</form>
</body>
</html>

package struts2.com.result;

import com.opensymphony.xwork2.ActionSupport;

public class RedirectParse extends ActionSupport {
		private String name;

		public String getName() {
				return name;
		}

		public void setName(String name) {
				this.name = name;
		}

		@Override
		public String execute() throws Exception {
				System.out.println(name);
				return SUCCESS;
		}
}

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>获取页面跳转后的值</title>
</head>
<body>
值:<s:property value="name"/>
</body>
</html>
 <action name="result_1" class="struts2.com.result.RedirectParse" >
        <result type="redirect">
<!--        <param name="location">getRedirectParse.jsp</param>-->
            <param name="location">page/result/getRedirectParse.jsp</param>
            <param name="parse">true</param>
        </result>
    </action>

dispatcher输出:

值:teayeon

redirect输出:

值:

两种方式控制台都输出:

teayeon

通过上面的执行结果我们可以看出因为dispatcher是将请求转发了的,所以他能够保存请求的数据,但是redirect是重新定义的一个请求地址,会失去所有请求数据,所以页面上就获取不到name的值。和servlet中的重定向和请求转发性质一样,只不过这里是封装起来了。可以参考我之前写的servlet的页面跳转来理解。


注意:请求转发dispatcher是在之前的url上跳转的,比如我上面的这个程序两个jsp页面是在一个目录下的,所以跳转只需要写jsp文件名.jsp就可以,但是使用重定向redirect来跳转,则需要我们写出全路径名从webapp之后开始。


小知识点:由于采用redirect重定向的方式来跳转,会取不到最初请求页面的值,如果要传值的话我们可以利用get的方式来传参:

result type="redirect">
            <param name="location">page/result/getRedirectParse.jsp?name=${name}</param>
            <param name="parse">true</param>
</result>
  • 这里涉及到了值栈的问题,后面会说到(${name})。
  • Struts2是在有请求到达的时候为每个请求创建一个新的值栈,也就是说值栈和请求是一一对应的,值栈封装了一次请求所需要的所有数据
  • 其实这里我们还是取不到值的,这里Struts2的标签会到值栈里取值,而这里是执行Result的时候,才在请求上 添加的name参数,然后就直接回到跳转的页面了,根本不会再走一次Struts2的运行过程,也就是说,这里传递的这个参数,根本不会进入到这个请求对应的值栈,因此这样写是取不到值的。所以我们可以利用servlet或者EL来获取值。

使用redirect的ResultType,可以访问其他的web应用中的web资源,跨服务器访问

chain的ResultType
基本使用
  <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
  1. chain是一种特殊的视图结果,用来将Action执行完之后链接到另一个Action中继续执行,新的Action使用上一个Action的上下文,数据也会被传递。
  2. 当遇到一个请求需要多个action处理才能完成的请求,那么这个ResultType就很常用了。不会立即响应,而是将数据传递到下一个Action中处理。这里的请求都是同一个请求。

请求页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>使用chain传递参数在Action中</title>
</head>
<body>
<form action="result_2">
    姓名:<input type="text" name="name">
    <input type="submit" value="提交">
</form>
</body>
</html>

第一个处理请求的action

package struts2.com.result;

import com.opensymphony.xwork2.ActionSupport;

public class ChainParse extends ActionSupport {

		@Override
		public String execute() throws Exception {
				System.out.println("我是chain的传递者。");
			return SUCCESS;
		}
}

第二个处理请求的action

package struts2.com.result;

import com.opensymphony.xwork2.ActionSupport;

public class ChainParse1 extends ActionSupport {
		private String name;

		public String getName() {
				return name;
		}

		public void setName(String name) {
				this.name = name;
		}

		@Override
		public String execute() throws Exception {
				System.out.println("我是chain的接收者。");
				return SUCCESS;
		}
}


作为请求的第一个处理action我们的result元素要跳转的位置就是下一个要处理请求的action元素的name属性,这样才能链接起来。

    <action name="result_2" class="struts2.com.result.ChainParse">
        <result type="chain">
            <param name="actionName">ChainParse1</param>
        </result>
    </action>
    <action name="ChainParse1" class="struts2.com.result.ChainParse1">
        <result >
            <param name="location">getchainparse.jsp</param>
        </result>
    </action>

接收参数页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>获取chain传递处理后的参数</title>
</head>
<body>
值:<s:property value="name"/>
</body>
</html>

控制台输出

我是chain的传递着。
我是chain的接收者。

从上面我们可以看出使用chain来作为ResultType的类型,可以传递一次请求的参数,而不是直接响应,可以经过多个Action来处理一个请求,这样可以让每一个Action来实现自己特有的功能。注意这里我们在Action中互相跳转的时候不能传递参数即不能出现地址后面跟?参数;这种形式的东西。

chain的参数namespace

参数namespace是可以保证我们可以访问不同包里的action来作为跳转。

    <param name="namespace">其他package里的namespace</param>
RedirectAction的ResultType
基本使用
  1. 这种结果类型与Redirect结果类型的行为有几分相似,但RedirectAction不是重定向到另一个资源,而是重定向到另一个Action。
  2. 有以下几种参数
    • actionName:用来指定”目的“动作的名字。他是RedirectAction结果类型的默认属性。
    • namespace:用来指定”目的“动作的命名空间,可以跨包跳转。
  3. 既然是重定向所以这样传递的请求一定会失去原来的值的。
FreeMarker的ResultType
  1. Struts2除了支持jsp作为视图技术之外,还支持其他的模版技术,比如FreeMarker和Velocity。
  2. FreeMarker是一个纯java模版引擎,是一种基于模版来生成文本的工具。
  3. FreeMarker配置如下:
   <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
  1. 示例
    • 制作FreeMarker的页面welcome.ftl
     <html>
     <head>
         <title>FreeMarker</title>
     </head>
     <body>
     helloword${name}!
     </body>
     </html>
    
  2. Freemarker的参数
    • location:用于指定FTL文件的位置
    • parse:默认为true,指定NOGL能够使用
    • contentType:默认为text/html,指定输出方式
    • writeIfCompleted:默认为false,指定是否不存在FTL解析错误的时候写入到流中。
  3. 我们要注意一下welcome.ftl应该放在哪里?
    • FreeMarker对应的FTL文件,并不是直接放在项目下,而是放在classes的路径下。
其他的ResultType
  • velocity:用来处理velocity模版,Velocity是一个模版引擎,可以将Velocity模版转换成数据流的形式,直接通过javaServlet输出。
  • xslt:用来处理XML/SLST模版,将结果转换为xml输出。
  • httpheader:用来控制特殊HTTP行为。
  • stream:用来向浏览器进行流输出

三、全局Result

全局Result概述
  1. 以前的<result>都是以<action>的子元素出现的,这被成为局部Result,只可以有本部的action元素使用。
  2. 当我们需要一个Result被多个Action共用时,我们不用麻烦的为每一个action元素都写上同一个result元素,这个时候我们就可以使用全局Result,让多个Action公用;比如多个界面判断该用户是否登陆,如果没有则需要跳转到登陆页面。
配置和使用
  1. 全局Result和局部的Result没有什么大的区别,同样是配置name和type。只不过这里的result元素不再是action元素的子元素,而是作为<global-results>元素的子元素,而<global-results>元素又是<package>元素的子元素,比如:
    <global-results>
        <result name="global" >login.jsp</result>
    </global-results>

在实际使用中我发现全局的result不能放在action的后面,一定要放在他们的前面,我是用的idea开发的,会在struts.xml文件中报红出错。

搜寻Result的顺序
  1. 在有了全局Result之后,需要讨论的是当Action运行之后,根据execute方法的返回值,会去对应struts.xml文件里的Result。那么是怎样的一噶顺序呢?
    • 首先,先找自己action元素里的result元素是否有匹配的name,如果没有则继续查找
    • 其次,再找自己package里的全局result,如果没有则继续向上找。
    • 再次,递归寻找自己继承的父包、祖父包中的全局result,如果没有则继续查找。
    • 最后,如果上述3种情况都没有找到对应的result,则会抛出Exception。
  2. 在开发中我们常常会使用子模块的struts配置文件,然后再classes下的struts.xml配置文件里引用进来。这里我们可以把所有子模块中的全局Result抽出来,放在他们的一个公用的父包里,然后struts.xml文件引用该父包就好,这样看起来自己的配置文件更加的整洁。

四、使用通配符

  1. 这里的通配符也适用于action。比如:
    <action name="*_*" class="struts2.com.result.ChainParse.{1}" method="{2}">
    </action>

name属性中的”*“代表长度不为0的任意字符串,因此它可以响应的Action只需要名称中间有一个下划线就行。比如使用helloworld_create作为访问的Action的话,那么第一个通配符 *就代表匹配helloworld,第二个通配符代表create。然后在具体要使用的地方我们可以使用{i}i代表下标,从1开始,来使用。

五、Struts2的异常映射

异常映射的基础

在Action中的execute方法签名为public String execute() throws Exception,这样,Action可以抛出任何异常,那么它是抛给谁了呢?

自己实现异常处理
  1. 比如我们自己写一个会报异常的Action
package struts2.com.result;

import com.opensymphony.xwork2.ActionSupport;

public class Exception_1 extends ActionSupport {
		@Override
		public String execute() throws Exception {
		int i =5/0;
		return SUCCESS;
		}
}
  1. 运行之后发现这个异常会抛给Struts2去处理。但是Struts2也没有去处理这个异常而是直接抛给了Web容器,在实际的项目中很显然不能这么简单粗暴的处理这些异常,一种简单的处理方法就是跳转到一个自定义的错误页面,提示用户或者管理员。
  2. 这里我们只要指定了异常跳转的页面,程序就会正常的运行下去了。
  • 处理异常的Action
package struts2.com.result;

import com.opensymphony.xwork2.ActionSupport;

public class Exception_1 extends ActionSupport {
		@Override
		public String execute() throws Exception {
				try {
						int i = 5 / 0;
				} catch (Exception e) {
						e.printStackTrace();
						return "exception";
				}
				return SUCCESS;
		}
}

这里的catch可以写的更具体一点,比如你知道会抛出什么异常,注意子异常要写在父异常的上边。

  • result配置
    <action name="exception" class="struts2.com.result.Exception_1">
        <result>Exception_1.jsp</result>
        <result name="exception">Exception_2.jsp</result>
    </action>
  • 错误页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>处理异常的页面</title>
</head>
<body>
<h2 style="color: red;align-content: center">出错啦!</h2>
</body>
</html>

这时当出现异常的时候就会跳转到当前页面,让程序走完。

使用Struts2的异常机制
  1. 之前的介绍中我们都没有接触到Struts2的异常机制,是我们自己写的异常处理方法。
  2. 在<action>元素中设置<exception-mapping>元素,可以指定在execute方法抛出指定错误的时候,跳转在那么页面。
  3. 修改上面的代码,我们不需要书写try-catch了,使用Struts2的异常处理机制就可以解决。
package struts2.com.result;
import com.opensymphony.xwork2.ActionSupport;
public class Exception_2 extends ActionSupport {
		@Override
		public String execute() throws Exception {
				int i = 5 / 0;
				return SUCCESS;
		}
}

 <action name="exception1" class="struts2.com.result.Exception_2">
    <result>Exception_1.jsp</result>
    <exception-mapping exception="java.lang.Exception" result="exception1"/>
    <result name="exception1">Exception_2.jsp</result>
 </action>

在Action里不用去自己捕获处理异常,直接让Action抛给Struts2,这时Struts2在struts.xml配置文件里配置了当请求的处理Action发生异常时就会找到对应的异常类型,然后映射一个result名称,通过该result名称调用result元素的页面。

局部异常映射与全局异常映射
  1. 上面的<exception-mapping>元素是作为action元素的子元素来使用的,只对该action元素有效,我们也可以配置全局的异常映射。
  2. 全局异常映射任然是<exception-mapping>元素,只不过不再是action元素的子元素,而是 <global-exception-mappings>元素的子元素,而 <global-exception-mappings>也是package的直接子元素。
    <global-exception-mappings>
            <exception-mapping exception="" result=""></exception-mapping>
        </global-exception-mappings>
  1. 当然配置<global-exception-mappings>,自然也要配置<global-results>,而且<global-results>必须配置在<global-exception-mappings>之前。
  <global-results>
            <result name="globalexception">exception.jsp</result>
  </global-results>

  <global-exception-mappings>
            <exception-mapping exception="java.lang.Exception" result="globalexception"></exception-mapping>
   </global-exception-mappings>

这里的局部异常和全局异常处理的查找顺序和前面的全局Result查找顺序是一致的。

在页面上输出异常信息
  1. 当我们跳转到指定的异常处理页面后,我们有可能需要输出一些异常的信息,为什么报异常。
  2. 我们可以使用Struts2提供的标签,来输出Exception的错误信息,比如:
    • <s:property value="exception"/><%--打印出exception的消息--%>
    • <s:property value="exceptionStack"/><%--打印出exception的堆栈消息--%>

五、PreResultListener

什么是PreResultListener?
  1. PreResultListener监听的事件是发生在Action处理完之后Result处理之前的,在创建事件监听类的时候我们需要实现PreResultListener的类。
PreResultListener实现示例
  1. 先要书写一个类来实现PreResultListener,创建事件监听处理类。
package struts2.com.result;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.PreResultListener;

public class PreResultListener_1 implements PreResultListener {
		@Override
		public void beforeResult(ActionInvocation invocation, String resultCode) {
				System.out.println("我是事件监听函数");
		}
}
  1. 书写一个Action类
package struts2.com.result;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.interceptor.PreResultListener;

public class PreResultListenerTest extends ActionSupport {
		@Override
		public String execute() throws Exception {
				System.out.println("我是Action处理类");
				PreResultListener listener=new PreResultListener_1();
				ActionInvocation context=ActionContext.getContext().getActionInvocation();
				context.addPreResultListener(listener);
				return SUCCESS;
		}
}

  1. 书写提交测试页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>测试监听事件的提交页面</title>
</head>
<body>
<form action="listener.action">
    <input type="submit" value="提交">
</form>
</body>
</html>
  1. 配置struts.xml
  <action name="listener" class="struts2.com.result.PreResultListenerTest">
            <result>listenerResult.jsp</result>
        </action>
  1. 书写result跳转页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>监听器触发之后跳转的页面</title>
</head>
<body>
<%
    System.out.println("我是result的页面");
%>
</body>
</html>
  1. 输出结果
我是Action处理类
我是事件监听函数
我是result的页面

七、自定义Result

自定义Result的概述
  1. 自定义Result就是我们自己开发的ResultType,之前我们使用的是Struts2自带的一些ResultType。这里我们要学会自己定义Result。
开发自定义Result
  1. 开发自定义Result我们只需要实现Result接口,该接口定义如下:
public interface Result extends Serializable {

    public void execute(ActionInvocation invocation) throws Exception;

}

在execute方法里面书写具体的Result处理,就是如何去展示视图。这些数据在ActionInvocation(动作调用)里面都能获取到。 2. 示例

  • 书写MyResult类
package struts2.com.result;
 
 import com.opensymphony.xwork2.ActionInvocation;
 import com.opensymphony.xwork2.Result;
 
 //自定义result
 public class MyResult implements Result {
 		@Override
		public void execute(ActionInvocation invocation) throws Exception {
				System.out.println("我要处理的Result字符串是"+invocation.getResultCode());
		}
 }

只是简单的输出了Action返回的要处理的Result的字符串的值,至于想要获取跟多的值,可以通过ActionInvocation去获取ActionContext,在ActionContext里面封装这所有需要的值。 - 配置MyResult

   <!--        自定义Result-->
     <result-types>
         <result-type name="MyResult" class="struts2.com.result.MyResult"></result-type>
     </result-types>
     
       <!--        使用自定义Result-->
     <action name="myresult" class="struts2.com.result.MyResultAction">
         <result type="MyResult">myresulttest.jsp</result>
     </action>
- 控制台输出
我要处理的Result字符串是success

上面的页面使用的是之前的提交页面,可以自己书写。在具体的开发中我们也会很少使用到自定义Result,如果用到,那么我们将具体的Result操作写在execute方法中就可以了。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!