Struts2 源码分析——Result类实例

本章简言

上一章笔者讲到关于DefaultActionInvocation类执行action的相关知识。我们清楚的知道在执行action类实例之后会相关处理返回的结果。而这章笔者将对处理结果相关的内容进行讲解。笔者叫他们为Result类实例。如果还记得在上一章最后笔者说可以把处理action执行的结果简单的理解处理网页。而且还用红色标识。实际是处理跟表现层有关的内容。而不是页面上的内容。如HTML。即是MVC里面的C到V的内容。当然这还关系到配置文件里面的result元素节点信息。如strtus.xml文件等里面的action元素节点下的result信息。

Result类实例的来源

我们都知道action节点下会很多result节点。在笔者的脑中好像result节点常用的有俩个属性节点:name和type。那么result元素节点下到底有些什么呢?这就不得不去看一下相关的DTD文件。请找到对应的struts-2.5.dtd文件。查看一下下面的内容。

<!ELEMENT result (#PCDATA|param)*>
<!ATTLIST result
    name CDATA #IMPLIED
    type CDATA #IMPLIED
>

好了。好像只有上面讲到的俩个属性节点。但是可以看到他还有一个子节点param。那么为什么要讲到result节点呢?主要是上一章出笔者讲到action类实例执行之后,会去处理结果。哪怕是返回String类型的结果,最终也会获得对应的Result类实例并执行处理结果。关于Result接口的源码却显的非常的简单。如下

1 public interface Result extends Serializable {
2
3     public void execute(ActionInvocation invocation) throws Exception;
4
5 }

使用过struts2的人都知道result节点的type值有很多种。type值更是用来表示返回结果的类型。即是type值有多少种,返回的结果就有多少种。如果让笔者来记的话。说真的笔者输了。笔者主要是通过源码文件才能知道到底有多少种。找到源码文件struts-default.xml查看里面的package元素节点下的result-types节点。如下

<result-types>
            <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
            <result-type name="dispatcher" class="org.apache.struts2.result.ServletDispatcherResult" default="true"/>
            <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
            <result-type name="httpheader" class="org.apache.struts2.result.HttpHeaderResult"/>
            <result-type name="redirect" class="org.apache.struts2.result.ServletRedirectResult"/>
            <result-type name="redirectAction" class="org.apache.struts2.result.ServletActionRedirectResult"/>
            <result-type name="stream" class="org.apache.struts2.result.StreamResult"/>
            <result-type name="velocity" class="org.apache.struts2.result.VelocityResult"/>
            <result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/>
            <result-type name="plainText" class="org.apache.struts2.result.PlainTextResult" />
            <result-type name="postback" class="org.apache.struts2.result.PostbackResult" />
        </result-types>

好吧。struts2为我们提供了11种。认真的查看源码的人会看到一个属性default。如上面的name为“dispatcher”的result-types的default属性值为true。实际这个值表示为默认使用dispatcher类型的返回结果。同时我们也看到每一个类型对应的类。这个时候笔者就会有一个问题。action执行之后,他是什么样根据String类型的结果值找到对应的Result类实例。笔者就不得不去找上一章解了到的源码。即是createResult方法的源码。如果没有记错的话。从createResult方法我们知道Result类实例是通过ObjectFactory类的buildResult方法得到的。对应的源码如下

ObjectFactory类:

public Result buildResult(ResultConfig resultConfig, Map<String, Object> extraContext) throws Exception {
        return resultFactory.buildResult(resultConfig, extraContext);
    }

看到上面的代码我们就清楚要找ResultFactory接口的实例类。那么实现ResultFactory接口的类有StrutsResultFactory类和DefaultResultFactory类。到底是哪一个呢?这个问题当年笔者找了很久就是不明白他到底是什么知道用哪一个。当年我去查看了初始化Container容器时,发现好像关于了ResultFactory接口对应的类,只有注入一个DefaultResultFactory类。如下源码。可是项目运行起来跟踪下去。发现用的是StrutsResultFactory类实例。笔者闷了很久。按道理应该是DefaultResultFactory类实例。可是为什么用的是StrutsResultFactory类呢?后来又在struts-default.xml文件里面找到一个。即是StrutsResultFactory类。如下源吗。

DefaultConfiguration类的createBootstrapContainer方法。

builder.factory(ResultFactory.class, DefaultResultFactory.class, Scope.SINGLETON);

struts-default.xml文件:

<bean type="com.opensymphony.xwork2.factory.ResultFactory" name="struts" class="org.apache.struts2.factory.StrutsResultFactory" />

看过XmlConfigurationProvider类的源码就明白这边ResultFactory接口注入的名字值是“struts”。而前面初始化Container容器注入的名字值是“default”。所以Container容器里面有俩个关于ResultFactory接口的注册类。看样子光是找到了struts-default.xml文件里面ResultFactory接口对应的StrutsResultFactory类。还是不能说明为什么要用StrutsResultFactory类实例。另一个原因是因为笔者认为应该是调用名字值为“default”的注册类。为什么这样子讲呢?在于ObjectFactory类里面,setResultFactory方法上面用的是@Inject。即是用默认名字值为default的注册类。笔者也不清楚到底是经过多少时间。最后在DefaultBeanSelectionProvider类的源码里面找到了原因。原来在Container容器里面,名字值为default的注册类被名字值为struts的注册类替换了。源码请读者自己行查看吧。

既然ObjectFactory类可以用来生成对应的Result类实例。那么他做了些什么呢?这是笔者想要了解的关键点。上面ObjectFactory类的buildResult方法源码里面我们至少知道他是在调用了StrutsResultFactory类实例。现在我们只要去找StrutsResultFactory类源码就可以明白他做了什么。如下

 1  public Result buildResult(ResultConfig resultConfig, Map<String, Object> extraContext) throws Exception {
 2         String resultClassName = resultConfig.getClassName();
 3         Result result = null;
 4
 5         if (resultClassName != null) {
 6             result = (Result) objectFactory.buildBean(resultClassName, extraContext);
 7             Map<String, String> params = resultConfig.getParams();
 8             if (params != null) {
 9                 setParameters(extraContext, result, params);
10             }
11         }
12         return result;
13     }

我们可以看出首先他是根据result节点的配置信息来获得对应的类名。即是上面讲到的11种结果类型对应的类。然后生成Result类实例。哎!又跑回ObjectFactory类了。这次却是生成对象。在把对象转化为Result类实例。接着就是设置对应的参数了。最后返回Result类实例。

Result类实例的处理

经过上面的过程之后。我们可以得到对应的Result类实例。相信大家都知道Result接口对的类实例就有11种。笔者当然也不可能在这里把11种都哪出来讲。笔者只能把一些比较常用的Result类实例拿出来讲。

1.ServletDispatcherResult类是strust2默认的返回结果。即是dispatcher类型。ServletDispatcherResult类实现于StrutsResultSupport类。让我们一下StrutsResultSupport类的源码吧。知道他做了些什么。如下

1   public void execute(ActionInvocation invocation) throws Exception {
2         lastFinalLocation = conditionalParse(location, invocation);
3         doExecute(lastFinalLocation, invocation);
4     }

上面就是Result类实例执行的入口方法。StrutsResultSupport类把当前location进行了处理。也是把要转跳到哪一个网页。其中conditionalParse方法是用于处理表达式。如动态返回结果。不管如何最后是一个跳转的URL。然后调用doExecute抽象方法。好了。让我们看一下ServletDispatcherResult类doExecute方法的源码。

 1  public void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
 2         LOG.debug("Forwarding to location: {}", finalLocation);
 3
 4         PageContext pageContext = ServletActionContext.getPageContext();
 5
 6         if (pageContext != null) {
 7             pageContext.include(finalLocation);
 8         } else {
 9             HttpServletRequest request = ServletActionContext.getRequest();
10             HttpServletResponse response = ServletActionContext.getResponse();
11             RequestDispatcher dispatcher = request.getRequestDispatcher(finalLocation);
12
13             //处理跳转URL上面的参数。并把他增加action请求的参数集合里面。
14             if (StringUtils.isNotEmpty(finalLocation) && finalLocation.indexOf("?") > 0) {
15                 String queryString = finalLocation.substring(finalLocation.indexOf("?") + 1);
16                 Map<String, Object> parameters = getParameters(invocation);
17                 Map<String, Object> queryParams = urlHelper.parseQueryString(queryString, true);
18                 if (queryParams != null && !queryParams.isEmpty())
19                     parameters.putAll(queryParams);
20             }
21
22             // 如果不存在网址的话,就跳出404
23             if (dispatcher == null) {
24                 response.sendError(404, "result ‘" + finalLocation + "‘ not found");
25                 return;
26             }
27
28             //是否是一个action tag 就是网页里面的一个包含action
29             Boolean insideActionTag = (Boolean) ObjectUtils.defaultIfNull(request.getAttribute(StrutsStatics.STRUTS_ACTION_TAG_INVOCATION), Boolean.FALSE);
30
31             //最后跳转
32             if (!insideActionTag && !response.isCommitted() && (request.getAttribute("javax.servlet.include.servlet_path") == null)) {
33                 request.setAttribute("struts.view_uri", finalLocation);
34                 request.setAttribute("struts.request_uri", request.getRequestURI());
35
36                 dispatcher.forward(request, response);
37             } else {
38                 dispatcher.include(request, response);
39             }
40         }
41     }

这段代码很简单,处理相关的参数。最后跳转配置里面对应的网页。笔者就不多说了。

2.ServletRedirectResult类是也是Result接口的实例类。即是redirect类型。相信看名字就可以知道用于重定向。也实现于StrutsResultSupport类。笔者也看了源码。真不知道要讲些什么。主要是判断要重定向的finalLocation是不是完全的URL。如果不是就生成对应的完全URL。然后在去找result节点的参数,并把他的参数一起放到requestParameters集合里面。这样子下面才可以生成对应的带参数的URL。如http://xxxx/sss?xxx=xxx。最后重定向。

ServletRedirectResult类:

 1 protected void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
 2         ActionContext ctx = invocation.getInvocationContext();
 3         HttpServletRequest request = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST);
 4         HttpServletResponse response = (HttpServletResponse) ctx.get(ServletActionContext.HTTP_RESPONSE);
 5
 6         //判断是否为完全的URL。true不是完全的URL。
 7         //如果不是完全的URL就生成完全的URL
 8         if (isPathUrl(finalLocation)) {
 9             if (!finalLocation.startsWith("/")) {
10                 ActionMapping mapping = actionMapper.getMapping(request, Dispatcher.getInstance().getConfigurationManager());
11                 String namespace = null;
12                 if (mapping != null) {
13                     namespace = mapping.getNamespace();
14                 }
15
16                 if ((namespace != null) && (namespace.length() > 0) && (!"/".equals(namespace))) {
17                     finalLocation = namespace + "/" + finalLocation;
18                 } else {
19                     finalLocation = "/" + finalLocation;
20                 }
21             }
22
23             // if the URL‘s are relative to the servlet context, append the servlet context path
24             if (prependServletContext && (request.getContextPath() != null) && (request.getContextPath().length() > 0)) {
25                 finalLocation = request.getContextPath() + finalLocation;
26             }
27         }
28
29         //把result节点上的参数一起放到请求参数集合里面,
30         ResultConfig resultConfig = invocation.getProxy().getConfig().getResults().get(invocation.getResultCode());
31         if (resultConfig != null) {
32             Map<String, String> resultConfigParams = resultConfig.getParams();
33
34             List<String> prohibitedResultParams = getProhibitedResultParams();
35             for (Map.Entry<String, String> e : resultConfigParams.entrySet()) {
36                 if (!prohibitedResultParams.contains(e.getKey())) {
37                     Collection<String> values = conditionalParseCollection(e.getValue(), invocation, suppressEmptyParameters);
38                     if (!suppressEmptyParameters || !values.isEmpty()) {
39                         requestParameters.put(e.getKey(), values);
40                     }
41                 }
42             }
43         }
44
45         //根据上面的参数来生成带有参数的URL,
46         StringBuilder tmpLocation = new StringBuilder(finalLocation);
47         urlHelper.buildParametersString(requestParameters, tmpLocation, "&");
48
49         // add the anchor
50         if (anchor != null) {
51             tmpLocation.append(‘#‘).append(anchor);
52         }
53
54         finalLocation = response.encodeRedirectURL(tmpLocation.toString());
55
56         LOG.debug("Redirecting to finalLocation: {}", finalLocation);
57
58         sendRedirect(response, finalLocation);//重定向到对应的URL
59     }

3.ServletActionRedirectResult类是ServletRedirectResult类的一个子类。 即是redirectAction类型。这个类的做法就是把相应的action的URL处理好。在把URL转给子类ServletRedirectResult去处理。当然要去获得对应的action名字,空间命名,方法。然后生成完全的action的URL。

 1 public void execute(ActionInvocation invocation) throws Exception {
 2         actionName = conditionalParse(actionName, invocation);
 3         if (namespace == null) {
 4             namespace = invocation.getProxy().getNamespace();
 5         } else {
 6             namespace = conditionalParse(namespace, invocation);
 7         }
 8         if (method == null) {
 9             method = "";
10         } else {
11             method = conditionalParse(method, invocation);
12         }
13
14         String tmpLocation = actionMapper.getUriFromActionMapping(new ActionMapping(actionName, namespace, method, null));
15
16         setLocation(tmpLocation);
17
18         super.execute(invocation);
19     }

笔者就介绍这三个类吧。相信读者也知道要去找哪里类了。我们可以看到action类实例执行结束之后。就会去获得对应的Result类实例。当然这里Result类实例具体是哪个类。就有result节点上的配置信息来决定了。然后进行要应的处理。

本章总结

本章讲到Result类实例的知识点。知道了struts2内部有几种的结果类型。每一个类型是什么处理得到的结果信息。是跳转,还是重定向,一切都是strust.xml文件的result节点的信息了。

时间: 2024-10-07 23:52:32

Struts2 源码分析——Result类实例的相关文章

Struts2 源码分析——调结者(Dispatcher)之执行action

章节简言 上一章笔者写关于Dispatcher类如何处理接受来的request请求.当然读者们也知道他并非正真的执行action操作.他只是在执行action操作之前的准备工作.那么谁才是正真的执行action呢?本章笔者就带大家来看看StrutsExecuteFilter类的工作.在理解StrutsExecuteFilter类的工作之前,笔者还是希望大家回顾一下前一章讲到的request请求工作.为什么这样子讲呢?可以说StrutsExecuteFilter类的工作是建立在StrutsPrep

Struts2 源码分析——拦截器的机制

本章简言 上一章讲到关于action代理类的工作.即是如何去找对应的action配置信息,并执行action类的实例.而这一章笔者将讲到在执行action需要用到的拦截器.为什么要讲拦截器呢?可以这样子讲吧.拦截器的应用是sturts2核心的亮点之一.如果不明白拦截器是什么的话,那么你相当于没有学习过struts2.笔者本来想直接讲这一章的知识点.可是又怕读者可能对拦截器没有一个概念化的理解.为什么这么讲呢?struts2在设计拦截器这一个部分的内容.在笔者看来事实是以AOP为核心思想来设计的.

Struts2 源码分析——配置管理之PackageProvider接口

本章简言 上一章讲到关于ContainerProvider的知识.让我们知道struts2是如何注册相关的数据.也知道如何加载相关的配置信息.本章笔者将讲到如何加载配置文件里面的package元素节点 .如struts.xml文件里面的package节点.那么为什么要分开来讲呢?按道理的话,都是放在上一章中一起讲不是更好吗?关键点在于笔者也不明白strtus2的作者为什么会这样子设计,把关于加载package元素节点信息的工作独立出来.而他的接口便是PackageProvider接口.但是不管如

Struts2 源码分析——过滤器(Filter)

章节简言 上一章笔者试着建一个Hello world的例子.是一个空白的struts2例子.明白了运行struts2至少需要用到哪一些Jar包.而这一章笔者将根据前面章节(Struts2 源码分析——核心机制)里的机制图片来分析源码.如果还不明白核心机制的朋友,请转到对应的章节进行阅读.笔者为了方便读者阅读,也把图片在次贴到了本章中.如下 根据图片笔者就明白我们首要分析便是橙黄色(Servlet Filters).也就是传说的过滤器(Filter).相信看过笔者前面几个章节的读者都明白strut

Struts2之struts2源码分析

一.学习案例:struts2讲到这基本上可以结束了.不知道大家有什么感觉呢,反正我是都忘了,你呢.别说你都记住了.如果真是记住了,那好,请在本文下方留下大名和照片,我要贴在床头,每日加以膜拜.呵呵.回到正题,struts2一些基本的东西,在之前的文章基本都解释,并配上项目演示了.不是说,struts2的博文结束了,你的学习就结束了,我也说了,我都忘了,你如果不忘,可真就不是一般人了.就像张三丰当年传太极与张无忌一样,正常人学东西都这样的.忘了就复习呗.好了,接下来还有一章都是关于struts2源

AppWidget源码分析---接口类

最近项目中接触到AppWidget,相对来说这部分比较简单,所以趁着空余时间详细阅读了AppWidget的源码.这篇文章主要是从源码上分析AppWidget中API类的相关原理,相关类的简单功能介绍和实现原理.关于使用,建议看指导文档. 简述 AppWidget相关的API类(供我们应用开发者使用的类)主要有: AppWidgetProvider:继承这个类,来提供Appwidget. AppWidgetManager:提供了AppWidget的管理接口,比如更新,绑定AppWidget id,

Struts2 源码分析——项目分析

项目知识点分析 从上一章中我们知道了接下来我们要去了解源码的项目(struts2-showcase).而这一章将讲述我三年后在次接触struts2-showcase项目是一个什么样子的情况.我有一个工作习惯.在参于每一个新项目开发的时候,我都希望项目负责人能跟我讲讲这个项目到底用到了哪些知识点.这样子的目标是让我心中对项目会有一个大体上的概念.悲剧的是struts2-showcase作者是不可能为你这个小小的角色特意打电话或是EMAIL你的.所以我的入手点是看这个项目的需要的JAR.如图下. 从

tornado框架源码分析---Application类之debug参数

先贴上Application这个类的源码. class Application(httputil.HTTPServerConnectionDelegate): """A collection of request handlers that make up a web application. Instances of this class are callable and can be passed directly to HTTPServer to serve the a

Struts2源码分析(一) 一一一 StrutsRequestWrapper

StrutsRequestWrapper的源码 // 核心代码: public class StrutsRequestWrapper extends HttpServletRequestWrapper { /** * 注意下面这句注释:获取object,如果没找到就去ValueStack里面找 * Gets the object, looking in the value stack if not found * * @param s The attribute key */ public Ob