核心分发器

9.2核心分发器 —— Dispatcher 

9.2.1 核心分发器(Dispatcher)的核心驱动作用 

Dispatcher之所以被称之为Struts2的核心分发器,主要是基于它在整个Struts2框架中的特殊地位。我们经常会使用“起-承-转-合”这4个不同的阶段来描述一个事件的整个过程,对于Struts2而言,Dispatcher实际上就是囊括这4个阶段的核心分发器。 

9.2.1.1起 —— 负责系统初始化 

Dispatcher在初始化时负责整个Struts2的初始化工作。在Dispatcher中,init()方法会在Dispatcher创建之初被调用,从而触发整个Struts2的初始化过程。所有的初始化方法在Dispatcher中都以init加上下划线作为起始进行命名,如图9-1所示: 

 

从图中,我们可以看到init方法是一个public方法,是整个初始化过程中的操作窗口。而其余的所有以init加上下划线命名的方法,都将在init方法中以一定的顺序被依次调用。这些方法涵盖了在Dispatcher进行初始化过程中逻辑的方方面面。我们将在之后的源码分析中为读者详细解读。 

9.2.1.2承 —— 接收并预处理Http请求 

Dispatcher需要负责对Http请求进行预处理。这些预处理的过程主要包括:设置Encoding和Locale、对HttpServletRequest进行封装以及准备MVC运行的数据环境等。 

这些预处理过程,由PrepareOperations在进行Http请求预处理的这个阶段调用执行。而我们知道PrepareOperations对象本身只是一个操作代理接口,真正完成逻辑功能的是Dispatcher本身。PrepareOperations在内部实现中所做的,只是对每个操作做了一个简单的转发处理。这些方法如图9-2所示: 

注意在这其中的两个createContextMap方法,在这两个方法中,Dispatcher把Web容器相关的数据对象,封装成了普通的Java对象(实际上被封装成了Map对象)。这对于整个框架的意义不亚于任何其他任何的精妙设计。因为这两个方法的核心要义在于将Web请求数据进行“去容器化”处理,使得后续依赖于Web请求的任何操作,不再受限于任何的Web容器对象,从而真正做到了解耦合。读者可以结合在之前XWork章节中,我们对ActionContext初始化的相关描述,createContextMap方法实际上成为了ActionContext这个数据环境内部构造上下文环境的数据基础。 

9.2.1.3转 —— 将Struts2转入XWork 

在第三章中,我们提到Struts2在处理Http请求时还可以被分为2个阶段,而划分这2个阶段的临界点实际上就在于Dispatcher对象。 

在第一个阶段中,所有的Http请求通过Dispatcher对象的Http请求的预处理,请求中与Web容器相关的对象全部被封装成与Web容器无关的对象,并构造出一个数据环境。于是,在第二个阶段中Dispatcher就能够将这一数据环境转发到XWork框架中执行,从而保证解耦在这一刻彻底完成。Dispatcher中进行请求转发的方法是serviceAction,如图9-3所示: 

 

我们可以看到,serviceAction方法与Servlet规范中的service方法有着异曲同工之妙。只是在这里,serviceAction方法并不是Http请求处理的“终点”,反而是Struts2 / XWork中进行Http请求处理的“起点”和“中转站”。 

serviceAction方法是整个Dispatcher对象的逻辑调度核心方法,也是整个Struts2在Http请求处理阶段的核心逻辑。我们将在下一章中对这个方法进行源码解读。 

9.2.1.4合 —— 垃圾清理 

Dispatcher不仅负责逻辑执行,还要负责在执行完毕之后进行对象清理。这一工作是由cleanup方法完成的,如图9-4所示: 

 

在Dispatcher的cleanup方法中,将完成对Http请求的处理过程中产生的请求周期的对象的清理工作。我们在这里不再展开其源码的分析,不过我们可以将其源码中所涉及到的清理对象进行逻辑分类: 

对于在整个请求周期中定义了完整的生命周期的框架元素的清理 

例如,我们之前讲过的XWork中Interceptor对象,其接口定义中就蕴含了destroy方法。这个方法,就会在cleanup的流程中被调用执行。 

对于线程安全的ThreadLocal对象的清理 

我们知道,XWork中的ActionContext作为一个数据环境使用了ThreadLocal模式在整个当前线程共享数据,那么在整个请求结束时,与当前线程绑定的ActionContext的副本也在cleanup方法中被清理。 

9.2.2 核心分发器(Dispatcher)的数据结构 

Dispatcher中的内容如此丰富,以至于它成为了我们进行Struts2研究的最重要线索。之前我们已经看到了Dispatcher在不同阶段所承担的不同职责。接下来,我们从数据结构的角度来看看Dispatcher有什么独特之处。翻开Dispatcher的源码,我们可以看到Dispatcher内部的属性构成,源代码如代码清单9-3所示: 

Java代码  

  1. public class Dispatcher {  
  2.       
  3.     // 这里省略了许多其它代码  
  4.   
  5.     /** 
  6.      * 提供了一个静态的ThreadLocal变量 
  7.      */  
  8. private static ThreadLocal<Dispatcher> instance = new ThreadLocal<Dispatcher>();  
  9.   
  10.     /** 
  11.      * 提供一个接口方法,用于获取当前线程安全的dispatcher实例 
  12.      * 
  13.      * @return  
  14.      */  
  15.     public static Dispatcher getInstance() {  
  16.         return instance.get();  
  17.     }  
  18.   
  19.     /** 
  20.      * 将dispatcher实例绑定到当前线程 
  21.      * 
  22.      * @param instance 
  23.      */  
  24.     public static void setInstance(Dispatcher instance) {  
  25.         Dispatcher.instance.set(instance);  
  26.     }  
  27. }  

这个非常惹眼的内部变量instance,它不仅是一个静态的实例变量,同时它也被定义成为ThreadLocal类型,而ThreadLocal所承载的实际类型是Dispatcher本身!这正是一个典型的ThreadLocal模式的应用范例! 

从这个数据结构的定义来看,我们可以确认,Struts2对Dispatcher应用了ThreadLocal模式。其中,Dispatcher本身就是实现ThreadLocal模式的核心要素,因为我们实际上在Dispatcher中看到了实现ThreadLocal模式的两大步骤。在之后的源码分析中,我们还将看到Dispatcher在处理Http请求时与ThreadLocal变量的交互过程。 

使用ThreadLocal模式的初衷是为了解决多线程环境下对象访问的线程安全问题。Dispatcher作为核心分发器,成为了入口程序StrutsPrepareAndExecuteFilter中的实例变量PrepareOperations和ExecuteOperations的实际操作句柄(Dispatcher是它们的重要实例变量),从而被所有的线程共享访问。因此,Dispatcher被打造成ThreadLocal模式是一个理所当然的选择。 

然而,这一理所当然的选择,却使得Struts2能够摆脱传统的表示层框架对于Web容器的高度依赖。由此可见,ThreadLocal模式是贯穿整个Struts2的核心技术之一,成为了Struts2将Web容器与Java开发解耦合的内在基础。不仅如此,我们还将在之后的章节中看到ThreadLocal模式在Struts2数据流转的过程中所起到的决定性作用。 

最后,有两个针对Dispatcher重要结论需要强调一下: 

downpour 写道

结论 Dispatcher作为一个线程安全的对象,涵盖了整个Struts2的生命周期。无论是Struts2的初始化,还是处理Http请求,实际都在Dispatcher中完成。

这个结论所针对的角度是Dispatcher的内容和职责,将它牢记于心,有助于日后我们对Struts2相关问题的调试和分析。实际上,对于Struts2的运行机理的一切研究,都离不开Dispatcher这个核心分发器。Dispatcher中所包含的所有方法,从一个侧面反应了Struts2框架的几个主要功能,而Dispatcher则处于一个“居中调度”的重要位置。 

downpour 写道

结论 Dispatcher是Struts2与XWork的分界点,也是将MVC实现与Web容器隔离的分界点。

这个结论则从另外一个角度描述了Dispatcher在整个Struts2框架中的重要地位。它也成为了Struts2实现“解耦合”的核心所在。我们将在Struts2的第二条主线的分析中看到这个分界点以及它在框架中的逻辑意义。 

原文链接:[http://wely.iteye.com/blog/2295281]

时间: 2024-11-18 14:44:50

核心分发器的相关文章

Yarn源码分析之事件异步分发器AsyncDispatcher

        AsyncDispatcher是Yarn中事件异步分发器,它是ResourceManager中的一个基于阻塞队列的分发或者调度事件的组件,其在一个特定的单线程中分派事件,交给AsyncDispatcher中之前注册的针对该事件所属事件类型的事件处理器EventHandler来处理.每个事件类型类可能会有多个处理渠道,即多个事件处理器,可以使用一个线程池调度事件.在Yarn的主节点ResourceManager中,就有一个Dispatcher类型的成员变量rmDispatcher,

在 muduo 中实现 protobuf 编解码器与消息分发器

本文是<一种自动反射消息类型的 Google Protobuf 网络传输方案>的延续,介绍如何将前文介绍 的打包方案与 muduo::net::Buffer 结合,实现了 protobuf codec 和 dispatcher. Muduo 的 下载地址: http://muduo.googlecode.com/files/muduo-0.1.9-alpha.tar.gz ,SHA1 dc0bb5f7becdfc0277fb35f6dfaafee8209213bc ,本文的完整代码可在线阅读

java随机事件分发器示例_java

以下需求:20%的概率去执行方法A,1%的概率去执行方法B,29%的概率去执行方法C,50%的概率去执行方法D 如果用if随机数之类的语句去判断的话,可能会写的很乱,我写了一个随机分发器,可以规范该操作.使用该分发器后的代码如下: 复制代码 代码如下:               //创建一个分发器RandomDispatcher randomDispatcher = new RandomDispatcher(); //设置每种事件的概率,注意,概率之和不一定是100哦randomDispatc

调度子系统2_核心调度器

// 核心调度器 // 当进程决定让出cpu时调用 // 函数任务: // 1.禁止内核抢占 // 2.获取本cpu的rq // 3.取消为当前进程运行的hrtimer // 4.获取队列锁 // 5.更新队列时钟 // 6.清除当前进程need resched标志 // 7.如果当前进程为非运行状态,并且当前非内核抢占路径 // 7.1 如果当前进程有信号待处理,设置当前进程为就绪状态 // 7.2 否则进程出队rq // 8.如果当前rq没有可运行进程 // 8.1 通过load balan

《Spring MVC学习指南(第2版)》——2.4 模型2之Filter分发器

2.4 模型2之Filter分发器 虽然servlet是模型2应用程序中最常见的控制器,但过滤器也可以充当控制器.但请注意,过滤器没有作为欢迎页面的权限.仅输入域名时不会调用过滤器分派器.Struts 2使用过滤器作为控制器,是因为该过滤器也用于提供静态内容. 下面的例子(appdesign2)是一个采用filter分发器的模型2应用,目录结构如图2.5所示. 图2.5 appdesign2目录结构 JSP页面和Product类同appdesign1相同,但没有采用servlet作为控制器,而是

Zend Framework教程之分发器Zend_Controller_Dispatcher用法详解_php实例

本文实例讲述了Zend Framework教程之分发器Zend_Controller_Dispatcher用法.分享给大家供大家参考,具体如下: 分发器的具体实现 Zend Framework的分发器Zend_Controller_Dispatcher设计主要有,如下类和接口组成: ├── Dispatcher │   ├── Abstract.php │   ├── Exception.php │   ├── Interface.php │   └── Standard.php Zend_Co

Zend Framework分发器用法示例_php实例

本文实例讲述了Zend Framework分发器用法.分享给大家供大家参考,具体如下: 分发是取得请求对象,提取其中的模块名.控制器名.动作名以及可选参数,然后实例化控制器并调用其中动作的整个过程. 如果其中的模块.控制器或动作没有找到,将使用默认值. Zend_Controller_Dispatcher_Standard类指定每个控制器和动作的默认值为index,模块的默认值为default. 该类允许开发者通过setDEfaultController()方法.setDefaultAction

Zend Framework分发器用法示例

本文实例讲述了Zend Framework分发器用法.分享给大家供大家参考,具体如下: 分发是取得请求对象,提取其中的模块名.控制器名.动作名以及可选参数,然后实例化控制器并调用其中动作的整个过程. 如果其中的模块.控制器或动作没有找到,将使用默认值. Zend_Controller_Dispatcher_Standard类指定每个控制器和动作的默认值为index,模块的默认值为default. 该类允许开发者通过setDEfaultController()方法.setDefaultAction

Yii核心验证器api详解

本文实例讲述了Yii核心验证器api.分享给大家供大家参考,具体如下: 核心验证器 Core Validators 前言 Yii 提供了一系列常用的核心 validators, 你可在 yii\validators namespace 中找到. 不用使用长的验证器类名, 你可以使用别名代替他们. 例如, 你可以使用别名 required 代替 yii\validators\RequiredValidator 类: <?php public function rules() { return [