本章简言

上一章笔者讲到关于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接口的源码却显的非常的简单。如下

 public interface Result extends Serializable {

     public void execute(ActionInvocation invocation) throws Exception;

 }

使用过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类源码就可以明白他做了什么。如下

  public Result buildResult(ResultConfig resultConfig, Map<String, Object> extraContext) throws Exception {
String resultClassName = resultConfig.getClassName();
Result result = null; if (resultClassName != null) {
result = (Result) objectFactory.buildBean(resultClassName, extraContext);
Map<String, String> params = resultConfig.getParams();
if (params != null) {
setParameters(extraContext, result, params);
}
}
return result;
}

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

Result类实例的处理

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

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

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

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

  public void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
LOG.debug("Forwarding to location: {}", finalLocation); PageContext pageContext = ServletActionContext.getPageContext(); if (pageContext != null) {
pageContext.include(finalLocation);
} else {
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
RequestDispatcher dispatcher = request.getRequestDispatcher(finalLocation); //处理跳转URL上面的参数。并把他增加action请求的参数集合里面。
if (StringUtils.isNotEmpty(finalLocation) && finalLocation.indexOf("?") > 0) {
String queryString = finalLocation.substring(finalLocation.indexOf("?") + 1);
Map<String, Object> parameters = getParameters(invocation);
Map<String, Object> queryParams = urlHelper.parseQueryString(queryString, true);
if (queryParams != null && !queryParams.isEmpty())
parameters.putAll(queryParams);
} // 如果不存在网址的话,就跳出404
if (dispatcher == null) {
response.sendError(404, "result '" + finalLocation + "' not found");
return;
} //是否是一个action tag 就是网页里面的一个包含action
Boolean insideActionTag = (Boolean) ObjectUtils.defaultIfNull(request.getAttribute(StrutsStatics.STRUTS_ACTION_TAG_INVOCATION), Boolean.FALSE); //最后跳转
if (!insideActionTag && !response.isCommitted() && (request.getAttribute("javax.servlet.include.servlet_path") == null)) {
request.setAttribute("struts.view_uri", finalLocation);
request.setAttribute("struts.request_uri", request.getRequestURI()); dispatcher.forward(request, response);
} else {
dispatcher.include(request, response);
}
}
}

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

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

ServletRedirectResult类:

 protected void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
ActionContext ctx = invocation.getInvocationContext();
HttpServletRequest request = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST);
HttpServletResponse response = (HttpServletResponse) ctx.get(ServletActionContext.HTTP_RESPONSE); //判断是否为完全的URL。true不是完全的URL。
//如果不是完全的URL就生成完全的URL
if (isPathUrl(finalLocation)) {
if (!finalLocation.startsWith("/")) {
ActionMapping mapping = actionMapper.getMapping(request, Dispatcher.getInstance().getConfigurationManager());
String namespace = null;
if (mapping != null) {
namespace = mapping.getNamespace();
} if ((namespace != null) && (namespace.length() > 0) && (!"/".equals(namespace))) {
finalLocation = namespace + "/" + finalLocation;
} else {
finalLocation = "/" + finalLocation;
}
} // if the URL's are relative to the servlet context, append the servlet context path
if (prependServletContext && (request.getContextPath() != null) && (request.getContextPath().length() > 0)) {
finalLocation = request.getContextPath() + finalLocation;
}
} //把result节点上的参数一起放到请求参数集合里面,
ResultConfig resultConfig = invocation.getProxy().getConfig().getResults().get(invocation.getResultCode());
if (resultConfig != null) {
Map<String, String> resultConfigParams = resultConfig.getParams(); List<String> prohibitedResultParams = getProhibitedResultParams();
for (Map.Entry<String, String> e : resultConfigParams.entrySet()) {
if (!prohibitedResultParams.contains(e.getKey())) {
Collection<String> values = conditionalParseCollection(e.getValue(), invocation, suppressEmptyParameters);
if (!suppressEmptyParameters || !values.isEmpty()) {
requestParameters.put(e.getKey(), values);
}
}
}
} //根据上面的参数来生成带有参数的URL,
StringBuilder tmpLocation = new StringBuilder(finalLocation);
urlHelper.buildParametersString(requestParameters, tmpLocation, "&"); // add the anchor
if (anchor != null) {
tmpLocation.append('#').append(anchor);
} finalLocation = response.encodeRedirectURL(tmpLocation.toString()); LOG.debug("Redirecting to finalLocation: {}", finalLocation); sendRedirect(response, finalLocation);//重定向到对应的URL
}

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

 public void execute(ActionInvocation invocation) throws Exception {
actionName = conditionalParse(actionName, invocation);
if (namespace == null) {
namespace = invocation.getProxy().getNamespace();
} else {
namespace = conditionalParse(namespace, invocation);
}
if (method == null) {
method = "";
} else {
method = conditionalParse(method, invocation);
} String tmpLocation = actionMapper.getUriFromActionMapping(new ActionMapping(actionName, namespace, method, null)); setLocation(tmpLocation); super.execute(invocation);
}

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

本章总结

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

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

  1. Struts2 源码分析——DefaultActionInvocation类的执行action

    本章简言 上一章讲到关于拦截器的机制的知识点,让我们对拦截器有了一定的认识.我们也清楚的知道在执行用户action类实例之前,struts2会先去执行当前action类对应的拦截器.而关于在哪里执行a ...

  2. Struts2 源码分析——Action代理类的工作

    章节简言 上一章笔者讲到关于如何加载配置文件里面的package元素节点信息.相信读者到这里心里面对struts2在启动的时候加载相关的信息有了一定的了解和认识.而本章将讲到关于struts2启动成功 ...

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

    本章简言 上一章讲到关于action代理类的工作.即是如何去找对应的action配置信息,并执行action类的实例.而这一章笔者将讲到在执行action需要用到的拦截器.为什么要讲拦截器呢?可以这样 ...

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

    本章简言 上一章讲到关于ContainerProvider的知识.让我们知道struts2是如何注册相关的数据.也知道如何加载相关的配置信息.本章笔者将讲到如何加载配置文件里面的package元素节点 ...

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

    章节简言 上一章笔者写关于Dispatcher类如何处理接受来的request请求.当然读者们也知道他并非正真的执行action操作.他只是在执行action操作之前的准备工作.那么谁才是正真的执行a ...

  6. Struts2 源码分析——配置管理之ContainerProvider接口

    本章简言 上一章笔者讲到关于Dispatcher类的执行action功能,知道了关于执行action需要用到的信息.而本章将会讲到的内容也跟Dispatcher类有关系.那就是配置管理中的Contai ...

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

    章节简言 上一章笔者试着建一个Hello world的例子.是一个空白的struts2例子.明白了运行struts2至少需要用到哪一些Jar包.而这一章笔者将根据前面章节(Struts2 源码分析—— ...

  8. Struts2 源码分析——Hello world

    新建第一个应用程序 上一章我们讲到了关于struts2核心机制.对于程序员来讲比较概念的一章.而本章笔者将会亲手写一个Hello world的例子.所以如果对struts2使用比较了解的朋友,请跳过本 ...

  9. JVM源码分析-类加载场景实例分析

    A类调用B类的静态方法,除了加载B类,但是B类的一个未被调用的方法间接使用到的C类却也被加载了,这个有意思的场景来自一个提问:方法中使用的类型为何在未调用时尝试加载?. 场景如下: public cl ...

随机推荐

  1. "SQL Server does not handle comparison of NText, Text, Xml, or Image data types."

    "SQL Server does not handle comparison of NText, Text, Xml, or Image data types." sql2000 ...

  2. Quartz 2D在ios中的使用简述一:坐标体系

    Quartz 2D是一个二维图形绘制引擎,支持iOS环境和Mac OS X环境,官方文档:Quartz 2D Programming Guide. 一.坐标体系 这样的坐标体系就导致我们使用Quart ...

  3. Python黑帽编程2.3 字符串、列表、元组、字典和集合

    Python黑帽编程2.3  字符串.列表.元组.字典和集合 本节要介绍的是Python里面常用的几种数据结构.通常情况下,声明一个变量只保存一个值是远远不够的,我们需要将一组或多组数据进行存储.查询 ...

  4. APOC 15 Years Celebration

    最近很忙,没有及时更新博客,也没有参加各种活动,唯一的活动就是接下来要讲的APOC 15 Years Celebration.不知不觉,自己也加入APOC有一年多了,正如大家所说“岁月是把杀猪刀”,我 ...

  5. Aspose.Words 16.8 破解版、添加自定义HTML导出Jpeg压缩质量配置

    0x01 Aspose.Words 介绍Aspose.Words是一个商业.NET类库,可以使得应用程序处理大量的文件任务.Aspose.Words支持Doc,Docx,RTF,HTML,OpenDo ...

  6. LeetCode Note 1st,practice makes perfect

    1. Two Sum Given an array of integers, return indices of the two numbers such that they add up to a ...

  7. WCF 安全性 之 None

    案例下载 http://download.csdn.net/detail/woxpp/4113172 服务端配置代码 <system.serviceModel> <services& ...

  8. php安装的一点点事 ---wampserver

    安装wampserver后,需要配置一些文件 1. 首先修改httpd.conf <Directory /> Options FollowSymLinks AllowOverride No ...

  9. WPF 弹出UserControl

    UserControl正常情况下是不能被弹出的,而编写好的UserControl页面,为了查看效果,又需要弹出. 为了解决这个问题,UserControl需要一个Windows来接收. var win ...

  10. 简单java在线测评程序

    简单java程序在线测评程序 一.前言 大家过年好!今年的第一篇博客啊!家里没有网,到处蹭无线!日子过得真纠结!因为毕设的需求,简单写了一个java程序在线测评程序,当然也可以在本地测试. 二.思路 ...