在我前面的文章中,也试图总结过SSH,见 http://blog.csdn.net/shan9liang/article/details/8803989 ,随着知识的积累,总感觉以前说得比较笼统,思路不够清晰,所以打算写几篇文章再稍微深入地剖析一下SSH,只能说是稍微,因为这三个框架的架构设计和基本原理,并不是通过几篇文字,就能说出所以然的,时间和精力有限,只是希望能通过这几篇简练的文字使大家对ssh的认识再上一个台阶。
一、框架的本质
在说Struts2,Spring和Hibernate核心原理之前,我觉得应该先搞明白以下三个问题,简短概括如下:
1、什么框架?
框架并不是什么神圣的东西,它只是一组jar包而已,其本质是对jdk功能的扩展,包含了一系列最佳实践,作用是解决某个领域的问题。
从广义上说,jdk也可以看做一个复合框架,它提供的api同样是为了解决各个领域的问题,例如java io解决文件操作的问题,java socket解决网络通讯的问题等等。
2、框架从何而来?
从框架的本质看,框架的产生就是为了解决一个又一个在开发中所遇到的问题。不同的框架是为了解决不同领域的问题。
3、为什么要使用框架?
框架是因解决问题而来,而且它带来了解决某一领域问题的最佳实践,实际是无数程序员在经过了无数次尝试后,总结出来处理特定问题的特定方法,如果我们把每个程序员的自由发挥看做是一条通往成功的路径,最佳实践就是其中的最短路径,它能够极大地解放生产力。
二、框架的适用范围
Web开发模式中最普遍的一种是“分层开发模式”,分层开发模式是指,在开发J2ee程序时,将整个程序根据功能职责进行纵向划分,比较常见的划分方法是:表示层,业务层,持久层。具体不再赘述,做java web开发,这是基本常识了。根据业务需求作用域的不同,这种分层会产生不同程度的变化,可能会细化更多层,也可能会合并,不能为了分层而分层,一切脱离业务架构的设计都是虚幻的。
我们最为熟悉得Struts,Spring,Hibernate正是为了应对各个层次的编程问题的最佳实践。即Struts对应表示层,Hibernate对应持久层,而Spring比较特殊,我们暂时简单地认为它对应业务层,后面我们再详细讨论。
三、Struts2
说了这么多,终于进入文章的重心了。
从宏观看,Struts2是一个运行于Web容器的表示层框架,其核心作用是帮助我们处理http请求。
Struts2遵循Servlet标准,通过实现标准的Filter接口进行Http请求的处理,通过在web.xml指定这个实现类StrutsPrepareAndExecuteFilter,就可以将Struts2引入到应用中来。
而Filter的生命周期也成为我们对整个Struts2进行逻辑主线划分的主要依据。
主线1:Struts2初始化-----Filter的init方法驱动执行。
主线2:Struts2处理Http请求------Filter的doFilter方法驱动
示意图:
1、Struts2初始化
Struts2初始化只在web容器启动时执行一次,启动的成败关系整个web应用的启动成败。初始化主线贯穿Struts2对其内置对象的创建和缓存过程,将Struts2的运行环境完整地创建起来。
我们从“数据”和“行为”两个角度来分析,构成Struts2整个初始化过程的主要元素,就可以分为“数据结构的定义”和“初始化行为的操作接口”两部分。
(1)从数据结构定义的角度,Struts2围绕管理Struts2内置对象的容器展开,该容器成为初始化主线中的核心构成元素。而另一类配置元素PackageConfig作为事件请求映射的配置元素也可作为构成元素。接口定义和实现类分别为:Container,ContainerImp,PackageConfig。
(2)从初始化行为的操作接口的角度,则由另外两个元素完成,加载接口和构造器。接口为:ConfigurationProvider(使用多重继承将Container和PackageConfig两类配置加载接口进行统一),ContainerProvider(Container的配置加载接口,其实现类需要负责初始化容器中的所有对象),PackageProvider(PackageProvider接口,其实现类负责初始化用于处理事件请求的配置对象),ContainerBuilder(Container构造器,用于初始化时构造容器),PackageConfigBulider(PackageProvider构造器,用于初始化时构造PackageProvider)
还有两个辅助元素承载上述接口,驱动整个初始化流程:ConfigurationManager(配置行为操作代理类,包含所有ContainerProvider和PackageProvider的实现以及配置的结构化数据Configuration),Configuration(配置数据的管理类,运行时获取配置的基本接口,承载所有配置的结构化数据和操作方法)
初始化步骤详细示意图
2、Struts2处理Http请求
Struts2处理Http请求又分两个阶段:Http请求预处理阶段(以后统称阶段1),XWork执行业务逻辑(以后统称阶段2)
严格意义上说,Struts2是由两个不同的框架组成,一个是执行在阶段1的Struts2,负责将Web容器与MVC分离,,一个是执行在阶段2的XWork,真正的MVC实现。
(1)阶段1的主要元素
Dispatcher,整个Struts2的核心,被称为核心分发器,是Struts2进行http请求预处理的核心场所,更是将Http请求与web容器解耦并进行逻辑处理转发的执行驱动中心。
PrepareOperations,HTTP预处理类,进行Http请求预处理的操作集合
ExecuteOperations,HTTP处理执行类,进行Http请求逻辑处理的操作集合
(2)阶段2的主要元素
这一阶段,程序控制权交给了XWork框架,涉及XWork框架的七大元素,这七大元素构成一条生产线,完成对http请求的处理。
ActionProxy,整个生产线的入口,封装了所有执行细节。
ActionInvocation,生产线的调度者,负责调度整个生产线中各个元素的执行次序。
Interceptor,生产线上的工序,丰富生产线的功能。
Action,生产线上的核心工序,负责核心业务逻辑的调用或实现。
ActionContext,提供整个生产线需要的数据环境。
ValueStack,提供表达式计算的工具类,Xwork数据访问的基础。(后面细说)
Result,生产线末端设备,输出生产线的生产结果。
结合阶段1的核心分发器,根据七大元素的调用关系,我们可以得到如下示意图:
从图中,我们可以看出以下几点:
(1)Dispatcher是Xwork框架的调用者和驱动者
(2)XWork生产线依赖两个数据流:ActionContext和ValueStack
ActionContext是一个独立的数据结构,无论是请求参数,处理返回值,甚至一些远程的Web容器对象,都被封装在ActionContext内部,成为XWork执行所依赖的数据基础。值得注意的是,ActionContext采用ThreadLocal保证线程安全。
ValueStack本身也是一个数据结构,从属于ActionContext,主要是对OGNL计算进行扩展,因此,位于ActionContext之中的ValueStack则赋予了ActionContext进行数据计算的功能,从而使得ValueStack自身成为一个可以进行数据访问的环境。
插入一点OGNL相关的东西。
如果我们要数据在View层和java世界中互相流转,就回在“字符串”和“对象树”之间存在不匹配性,这个不匹配性源于Web是一个“弱类型”的平台,而java却是一个具有丰富数据类型的“强类型”的平台。同一个对象在两个平台之间交互,就必须要一个“翻译”,也就是我们说的“表达式引擎”,它充当着“翻译桥梁”的作用,保证数据能够顺利地在MVC的各个层次进行流转。而OGNL就是Struts2选择的表达式引擎。
(3)XWork生产线所依赖的控制流:事件处理驱动元素-----ActionProxy,ActionInvocation,事件处理节点-----Interceptor,Action, Result
有关这五大元素的作用和关系,我们用下面这个比喻来诠释。
四、本篇总结
至此,我们围绕初始化和处理Http请求两大主线说明Struts作为表示层框架的基本原理,用一张图概括本篇脉络
随着知识的积累,理解自然深入,下篇,我们将继续探究Hibernate的核心原理……