Struts2 源码分析——调结者(Dispatcher)之执行action
章节简言 |
上一章笔者写关于Dispatcher类如何处理接受来的request请求。当然读者们也知道他并非正真的执行action操作。他只是在执行action操作之前的准备工作。那么谁才是正真的执行action呢?本章笔者就带大家来看看StrutsExecuteFilter类的工作。在理解StrutsExecuteFilter类的工作之前,笔者还是希望大家回顾一下前一章讲到的request请求工作。为什么这样子讲呢?可以说StrutsExecuteFilter类的工作是建立在StrutsPrepareFilter类基础上运行的。先相信这一点笔者不需要声明了。笔者为了更好的理解小小的做一个张图片。如下
从上面的图片我们就是很清楚StrutsPrepareFilter类做了哪些工作。而图上的上五点对于后面的StrutsExecuteFilter类来讲是非常重要的。虽然我在前面几章也提过StrutsExecuteFilter类的知识。《Struts2 源码分析——过滤器(Filter)》章节里面也讲过。只是很简单的略讲一下。并没有对他进特别的讲。主要是笔者认为不了解StrutsPrepareFilter类的工作的情况下,去了解StrutsExecuteFilter类的话。是一件比较吃力的事情。好了。笔者就不多说了。让我们进入本章的内容吧。
调结者的执行action |
StrutsExecuteFilter类的工作就是执行对应的action请求。StrutsExecuteFilter类的工作还需要有一个叫ExecuteOperations类的帮助。如果看过源码的朋友都知道,StrutsExecuteFilter类的代码里用了ExecuteOperations类的俩个方法。一个是:executeStaticResourceRequest方法。一个是:executeAction方法。光从字名面上我就知道他们的功能。executeStaticResourceRequest是执行静态资源请求。如JS文件,css文件等。而executeAction就是执行action请求。即是笔者想要讲的重点。好了。还是让我们先看一下StrutsExecuteFilter类代码吧。如下部分代码
StrutsExecuteFilter类:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res; if (excludeUrl(request)) {//用于判断是否在排除的action之内。如果是就跳过。
chain.doFilter(request, response);
return;
} if (execute == null) {
lazyInit();//初始化相关的信息类。
} ActionMapping mapping = prepare.findActionMapping(request, response);//找到ActionMapping实例 Integer recursionCounter = (Integer) request.getAttribute(PrepareOperations.CLEANUP_RECURSION_COUNTER); if (mapping == null || recursionCounter > 1) {
boolean handled = execute.executeStaticResourceRequest(request, response);//执行请求css,js文件。并返回是否成功。
if (!handled) {
chain.doFilter(request, response);
}
} else {
execute.executeAction(request, response, mapping);//执行action请求,重要部分
}
}
根据上面的红色的代码,让笔者讲一下总共做了几件事件。
1.判断当前的request请求是不是被排在外。如果就跳过去。(笔者不想过讲,太简单了)
2.判断是否存在ExecuteOperations类的实例。如果没有就初始化。相关的代码如下。
StrutsExecuteFilter类:
/**
* 加载并初始化
*/
protected synchronized void lazyInit() {
if (execute == null) {
InitOperations init = new InitOperations();//用于初始化的功能类
Dispatcher dispatcher = init.findDispatcherOnThread();//StrutsPrepareFilter类的时候,就把Dispatcher实例存放在本地线程里面。这是只是把他拿出来。
init.initStaticContentLoader(new FilterHostConfig(filterConfig), dispatcher);//初始化用于加载css,js文件的加载类。 prepare = new PrepareOperations(dispatcher);
execute = new ExecuteOperations(dispatcher);
} }
看了代码我们就知道StrutsExecuteFilter类的lazyInit方法做了什么。
1).找到对应的Dispatcher实例。那么Dispatcher实例在哪里初始化呢?这就是StrutsPrepareFilter类的里面。(如果不理解的读者,请转至Struts2 源码分析——调结者(Dispatcher)之action请求的章节)
2).初始化StaticContentLoader类。即是用于加载JS,CSS文件等类似的加载类。
3).初始化相关对应的PrepareOperations类和ExecuteOperations类。为了下面执行action请求准备。其中ExecuteOperations类很重要。用于执行action和加载JS,CSS文件类似的调动者。
3.找到对应的action映射(ActionMapping类)。可以说没有action映射就没有办法执行相关的action操作。让我们看一下findActionMapping方法的代码吧。
PrepareOperations类:
public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {
ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);
if (mapping == null || forceLookup) {
try {
mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager());
if (mapping != null) {
request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);
}
} catch (Exception ex) {
dispatcher.sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
}
} return mapping;
}
先从request请求中找到以STRUTS_ACTION_MAPPING_KEY常量为Key的ActionMapping值。如果不存在,则通过Container容器中找到的ActionMapper实例,并通过ActionMapper实例找到对应的Action映射,并存于request请求。其Key值为STRUTS_ACTION_MAPPING_KEY常量。相信读者又看Dispatcher类的实例了。又跟他有关系。关于这一步其实在StrutsPrepareFilter类工作的时候就已经做过一次了。(在这里用到ActionMapper类。关于他的作用读者目前只要知道所有的struts.xml上的配置action信息都在里面。笔者后面说找一个章节讲他)
4.如果没有找到对应的action映射(ActionMapping类)或action跳越的数量>1就是执行加载JS,CSS文件的加载类。否则就是执行action。实话实说笔者真不知道recursionCounter > 1是什么个意思。我只能把他理解为跳转的action数。笔者也做了相关通的实验就是希望看出一些事端。可惜失败了。
先看一下executeStaticResourceRequest方法吧。对于executeStaticResourceRequest方法。笔者在上面就讲到了。他是用于加载相关的静态资源。如CSS文件,JS文件。这些文件是在JAR里面的。我们有时候struts2相关的UI的TAG的时候,就要加载对应的CSS文件,和JS文件吧。这个时候他就启作用了。让我们看一下代码吧。
ExecuteOperations类:
public boolean executeStaticResourceRequest(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
// 如果没有找到对应的action,我们应该看一下是不是请求静态资源
String resourcePath = RequestUtils.getServletPath(request); if ("".equals(resourcePath) && null != request.getPathInfo()) {
resourcePath = request.getPathInfo();
} StaticContentLoader staticResourceLoader = dispatcher.getContainer().getInstance(StaticContentLoader.class);
if (staticResourceLoader.canHandle(resourcePath)) {
staticResourceLoader.findStaticResource(resourcePath, request, response);
return true; } else {
// 如果不是的话,就表示他是一个普通的请求
return false;
}
}
因为这部分不是笔者这系列要讲的重点。如果有兴趣的读者可以自行继续研发下去。我们可以看又是跟Dispatcher类的实例有关系。相信读者这个时候很能明白笔者为什么说Dispatcher类很重要。很能做很多事情。
关于执行action的部分就在executeAction方法里面。让我们看一下代码吧。
ExecuteOperations类:
public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
dispatcher.serviceAction(request, response, mapping);
}
好吧。我有一种打人的冲动。Dispatcher类的实例又出现。执行request请求的action也是Dispatcher类的实例来完成的。既然如此让我们看一下代码吧。如下
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
throws ServletException { Map<String, Object> extraContext = createContextMap(request, response, mapping); //如果之前就有了值栈,就是新建一个新的值栈,放入extraContext
ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
boolean nullStack = stack == null;
if (nullStack) {
ActionContext ctx = ActionContext.getContext();
if (ctx != null) {
stack = ctx.getValueStack();
}
}
if (stack != null) {
extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
} String timerKey = "Handling request from Dispatcher";
try {
UtilTimerStack.push(timerKey);
String namespace = mapping.getNamespace();//获得request请求里面的命名空间,即是struts.xml是的package节点元素
23 String name = mapping.getName();//获得request请求里面的action名
24 String method = mapping.getMethod();//要执行action的方法 ActionProxy proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name,
method, extraContext, true, false);//获得action的代理 request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack()); 31 // 如果action映射是直接就跳转到网页的话,
32 if (mapping.getResult() != null) {
33 Result result = mapping.getResult();
34 result.execute(proxy.getInvocation());
35 } else {
36 proxy.execute();//这里就是执行action
37 } if (!nullStack) {
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
}
} catch (ConfigurationException e) {
logConfigurationException(request, e);
sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);
} catch (Exception e) {
if (handleException || devMode) {
sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
} else {
throw new ServletException(e);
}
} finally {
UtilTimerStack.pop(timerKey);
}
}
从上面的代码就能看出在执行action的内部还需要用到一个叫ActionProxy类。关于这部分知识,笔者其实这里不想讲的很细。主要这部分的知识太多了。但这里笔者还是希望为后面的章节做好准备。ActionProxy类可以理解他是一个代理。他的主要目地就是根据action映射得到的信息,寻找对应action类实例,然后执行对应的方法。其中包括加载对应的拦截器,初始化相应的结果。而这段代码中,在ActionProxy类的execute()方法的时候,还作了相应的判断。即是是否直接回返结果。其次还有在讲到一个关于值栈的知识。这里在获得ActionProxy类实例的时候,需要得到对应值栈的信息。但是不管如何,最后一定会把request请求的值栈重新更新一下。ValueStack(值栈)的作用相信大家都懂。我就不做过多的讲解了。
本章总结 |
可以说相关Dispatcher类的知识点,到本章节算是结束了。笔者把Dispatcher类的功能分为三点:一是加载struts2运行的必要条件信息;二是初始化action请求需要的信息;三是执行request请求对应的action。而关于核心机制图片的橙色部分的工作大部分笔者都有体现出来。而后面的章节都是为了这三个功能点进行的。所以希望读者能理解这三个功能。
Struts2 源码分析——调结者(Dispatcher)之执行action的更多相关文章
- Struts2 源码分析——调结者(Dispatcher)之action请求
章节简言 上一章笔者讲到关于struts2启动的时候加载对应的准备工作.如加载配置文件struts.xml之类的信息.而相应的这些操作都离不开Dispatcher类的帮助.如果读者只是认为Dispat ...
- Struts2 源码分析——调结者(Dispatcher)之准备工作
章节简言 上一章笔者讲到关于struts2过滤器(Filter)的知识.让我们了解到StrutsPrepareFilter和StrutsExecuteFilter的作用.特别是StrutsPrepar ...
- 调结者(Dispatcher)之执行action
调结者的执行action StrutsExecuteFilter类的工作就是执行对应的action请求.StrutsExecuteFilter类的工作还需要有一个叫ExecuteOperations类 ...
- Struts2 源码分析——Action代理类的工作
章节简言 上一章笔者讲到关于如何加载配置文件里面的package元素节点信息.相信读者到这里心里面对struts2在启动的时候加载相关的信息有了一定的了解和认识.而本章将讲到关于struts2启动成功 ...
- Struts2 源码分析——配置管理之ContainerProvider接口
本章简言 上一章笔者讲到关于Dispatcher类的执行action功能,知道了关于执行action需要用到的信息.而本章将会讲到的内容也跟Dispatcher类有关系.那就是配置管理中的Contai ...
- Struts2 源码分析——过滤器(Filter)
章节简言 上一章笔者试着建一个Hello world的例子.是一个空白的struts2例子.明白了运行struts2至少需要用到哪一些Jar包.而这一章笔者将根据前面章节(Struts2 源码分析—— ...
- Struts2 源码分析——配置管理之PackageProvider接口
本章简言 上一章讲到关于ContainerProvider的知识.让我们知道struts2是如何注册相关的数据.也知道如何加载相关的配置信息.本章笔者将讲到如何加载配置文件里面的package元素节点 ...
- Struts2 源码分析——Hello world
新建第一个应用程序 上一章我们讲到了关于struts2核心机制.对于程序员来讲比较概念的一章.而本章笔者将会亲手写一个Hello world的例子.所以如果对struts2使用比较了解的朋友,请跳过本 ...
- Struts2 源码分析——DefaultActionInvocation类的执行action
本章简言 上一章讲到关于拦截器的机制的知识点,让我们对拦截器有了一定的认识.我们也清楚的知道在执行用户action类实例之前,struts2会先去执行当前action类对应的拦截器.而关于在哪里执行a ...
随机推荐
- Delphi中stringlist分割字符串的用法
Delphi中stringlist分割字符串的用法 TStrings是一个抽象类,在实际开发中,是除了基本类型外,应用得最多的. 常规的用法大家都知道,现在来讨论它的一些高级的用法. 1.CommaT ...
- eclipse创建Maven-web项目(-)
一.new----other----maven----maven project 二.next 三.next(选择maven-archetype-webapp) 四.填写相应的信息,Packaged是 ...
- DevOps是云计算时代的开发与运营
DevOps(英文Development和Operations的组合)是一组过程.方法与系统的统称,用于促进开发(应用程序/软件工程).技术运营和质量保障(QA)部门之间的沟通.协作与整合.[1] 它 ...
- log4j2.xml实用例子
一个多月前,我写了篇关于log4j.xml配置的文章,点击此处查看:http://www.cnblogs.com/guogangj/p/3931397.html 最近,我把自己的log4j升级到2.0 ...
- Google分布式构建软件之一:获取源代码
本文原文在google开发者工具组的博客上[需要FQ],以下是我的翻译,欢迎转载,但尊重作者版权,注名原文地址. 在Google,所有的产品都是在主干上开发的.这样的好处:每个人都可以查看和修改代码, ...
- 三国魂破解1——resmask.swf
这个是资源文件的映射. 根据index.swf的加载流程,直到初始化会加载: 破解方式: var l:URLLoader = new URLLoader(); l.dataFormat = URLLo ...
- JavaScript利用replace更改所有符合条件字符
利用replace替换字符串时,在正常使用情况下默认只能更改匹配到的第一个字符 var a=new String("fffffddd"); console.log(a.replac ...
- Spark算子选择策略
摘要 1.使用reduceByKey/aggregateByKey替代groupByKey 2.使用mapPartitions替代普通map 3.使用foreachPartitions替代forea ...
- 【hadoop摸索系列】记录使用libhdfs访问hdfs的关键问题
hadoop官方的二进制发布版本一直是32位平台编译的,对于java来说跨平台不影响使用,但是为了在c/c++程序中操作hdfs就做不到了,因为libhdfs.so是二进制不兼容的. 我使用的是sta ...
- 【Win 10应用开发】Adaptive磁贴模板的XML文档结构
在若干天之前,老周给大家讲了Adaptive Toast通知的XML模板,所以相应地,今天老周给大家介绍一下Adaptive磁贴的新XML模板. 同样道理,你依旧可以使用8.1时候的磁贴模板,在win ...