- 浏览: 295843 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
zh554275855:
1 接口是核心,其定义了要做的事情,包含了许多的方法,但没有定 ...
抽象类和接口的区别,使用场景 -
MeowPass:
[color=red][size=xx-large][alig ...
java 字符串split有很多坑,使用时请小心!! -
jayzc1234:
讲的很好 看头像还是个女的 真是牛逼
WEBX学习总结 -
wodexiang:
写的什么狗屎
jetty启动以及嵌入式启动 -
繁星水:
很好,感谢分享与总结,谢谢!
jetty启动以及嵌入式启动
把对webx的学习总结搬到iteye上来
一、 WEBX框架的初始化
(一)、 一个请求如何进入到这个框架中来?在webx2中,它提供了一个servlet:WebxControllerServlet。这个servlet接收请求,处理请求,返回请求结果。
框架要运行前,得先初始化一些资源,资源的初始化是由servlet容器来做的。Servlet容器启动到初始化一个Servlet的过程如下:当Servlet容器(比如Jboss)启动时,它会解析web.xml文件,淘单项目中在web.xml中对WebxControllerServlet的配置如下:
图【1】
对于此配置,servlet容器会创建WebxControllerServlet对象,并且调用其抽象父类的init()方法,以完成该servlet的初始化。在AbstractWebxControllerServlet中,init()方法如下:
图【2】
图【3】
WebxLoader装载和初始化Webx中的services。在此,webxLoader读取servlet的上下文信息,然后在自身的configure()方法里初始化logging(日志)系统和service manager,如下图
图【4】
(二)、初始化日志系统
首先对日志系统进行一个说明:总体来说,webx2框架中有一个日志管理对象,框架中的所有日志事务都由该对象来处理,甚至在对WebxControllerServlet进行初始化时,也会用到它。理所当然,该对象成了WebxControllerServlet的成员变量(通过继承得到的)。
初始化过程如下:
(1) 取得日志输出的目录,即是图【1】中的以下初始化参数,这是在web.xml中对WebxControllerServlet进行配置时的初始化参数:
图【5】
当jboss启动之后,我们就可以到该目录下查看日志文件,taoshare-Debug.log,taoshare.log等等。
(2) 配置日志系统。对于日志管理对象,需要为其设置一些属性,而这些属性的配置放在/WEB-INF/log4j.xml中, 而webx2是使用WebappBootstrapResourceLoaderService对象加载资源的,理所当然,在配置log4j日志系统之前,先创建这个对象,然后再去加载log4j.xml文件并返回表示该资源的java.net.URL对象,接着解析该资源,至此就完成了日志系统的初始化。在log4J.xml中定义了日志的等级,淘分享项目中,只有大于debug等级的日志才会被打印到文件中。
图【6】
异常处理:但是在日志初始化完成之前以及日志初始化过程中出现了异常要记录一些信息,怎么办?webx2采用的方式是直接把要记录的信息输出到java的“标准”错误输出流中,如下:
图【7】
图【5】是应用程序日志输出目录,那么如果我们在日志初始化完毕之后,使用System.out.println(message)或System.err.println(message)打印消息,此时消息不会打印到dos窗口,而是打印到D:/home/admin/logs/taoshare.log文件中。这就是为什么我们在使用webx2框架时,如果在程序中打印消息,而消息输出到了taoshare.log文件中的原因。
(三)、装载webx配置文件
Webx2的Service的默认配置路径是:根目录/WEB-INF/webx.xml,这是主配置文件。此外配置文件也可放入各组件的配置文件夹下,其路径是:要目录/WEB-INF/组件名/webx.xml。
在加载webx配置文件过程中,resourceLoader首先加载默认路径下的webx.xml: /WEB-INF/webx.xml。然后加载各组件下的webx.xml:/WEB-INF/组件名/webx.xml。如下图:
图【8】
通过以上几步,webx配置文件就加载完毕了。但webx2还要加载一些默认的service,分为两部分
(1) Webx Tubine的配置文件:webx-turbine-default.xml,它是导入包自带的,
(2) Webx的默认配置文件webx-default.xml,它存放在/WEB-INF/webx-default.xml中。如图所示:
通过以上的处理之后,webx的配置文件全部加载完毕,并且这些配置文件的资源最终被抽象为一个Configuration对象,这个对象会在ServiceManager中。
(四)、初始化ServiceManager
通过(三)的配置文件加载,这些配置文件为我们提供了很多 Service,不同的Service都有不同的功能,而这些功能是框架出色完成请求处理的利器。但是如此多的Service,得对它们进行统一管理,以便在框架需要某些Service进行服务时,能通过Service的管理器获取该Service对象,让它供框架使用,比如框架在需要某个Service来提供相应功能时,但是目前框架中无可使用的该Service对象,此时Service管理器将初始化这个Service等等。Webx2就提供了一个默认的Service管理器:DefaultWebxServiceManager。
ServiceManager要管理Service,那么首先它得取得这些配置文件资源,所以在接下来的配置ServiceManger对象时,会首先将该Configuration对象交给ServiceManager。
图【10】
此外,在必要时,ServiceManager需要加载相关资源,所以它需要一个资源加载器,因此在配置ServiceManager时,webx2框架正是将(三)中用于加载webx配置文件的资源加载器交给了ServiceManager使用。
有了这些条件,初始化ServiceManager时机已经成熟。在初始化ServiceManager过程中,会做以下一些事情:
(1)通过对Configuration对象的处理,创建service名称和service配置的映射,完成之后,ServiceManager对所有的Service已经了如指掌,比如,某个Service的名字是什么,它的具体实现类是什么,初始化这个Service时应该给它初始化些什么属性等等,因为这些都已经配置在配置文件中了。
(2)初始化“资源加载服务”,这个Service在webx-default.xml中有配置,取出其中之一的配置:
二、 Webx框架的请求处理
(一)、当一个请求到达时候,容器会分析请求方法,然后调用WebxControllerServlet中对应的doXXX(HttpServletRequest,HttpServletResponse)方法。在webx2中,对于到来的请求,最终都会调用doGet()方法来处理,因为doPost()、doHead()方法都只是调用doGet()方法而已。
在doGet()方法中处理步骤如下:
1、 将请求封装为Rundata对象
webx2会将容器传入的HttpServletRequest和HttpServletResponse进行封装,将二者封装到Webx2提供的RunData对象中(封装由RunDataService进行处理)
为什么要将请封装成RunData?
封装之后在webx中就可以直接使用rundata来取参数 ,甚至在rundata中为请求做缓存。这样的封装可以更加个性化的使用request和response,根据不同的需要,可以在rundata中提供不同的方法,来满足我们对request和response的处理。比如rundata.getInt("param");这个方法在request中是没有的,这是rundata提供的。这样我们取参数就更加方便了。如果使用request,我们将使用request.getParameter("param");然后再把取得的值转成我们想要的类型
图【12】
RunDataService这个Service在webx-default.xml中配置如下
图【13】
2、 为每个请求保存一个RunData对象
得到RunData之后,在处理请求之前,webx2又对该RunData进行了处理。此时它将RunData放入到ThreadContextService这个Service之中。
图【14】
先构建请求再执行doGet()方法,
(三)、PipelineService处理请求
通过以上的准备工作,现在开始处理请求,因为请求是被封装在RunData里的,所以处理的结果也存放在RunData对象中。请求的处理是由PiplelineService完成PiplelineService Service配置在/WEB-INF/webx-default.xml中。
图【15】
PipelineService在初始化时,是使用ResourceLoaderService来加载资源的,对于这个service,在前面已经讲述了它的初始化。ResourceLoaderService所做的事情就仅仅是找到资源路径,然后交给底层的webx2资源加载器,完成资源的加载。那ResourceLoaderService是如何找到资源的呢?其实ResourceLoaderService提供了查找资源的功能,它提供这个功能的原因在于webx2框架开发者想让开发者可以将一些些复杂的资源路径与一个简短的资源路径进行映射。我们先来看ResourceLoaderService的配置,它的配置在webx-default.xml中可以找到,如下图:
resources.xml文件便是它使用的映射文件,来看看resources.xml文件中的部分内容:
图【16】
红框中的内容的意思就是,使用“/taoshare”这个路径代替复杂路径:“/webroot/WEB-INF”。
现在我们理清了PipelineService初始化时是如何找到资源的。PipelineService初始化时,会使用ResourceLoaderService去加载“/taoshare/pipeline.xml”资源。ResourceLoaderService在加载此资源时,它会首先解析出路径中的映射路径和资源名,即它会依据映射文件resources.xml解析出此路径中的映射路径部分:“/taoshare”和资源名:“pipeline.xml”;接着,它从映射文件中找到“/taoshare”所对应的实际资源路径:“/webroot/WEB-INF/”;接着将此实际资源路径与资源名组合得到资源的真正路径:“/webroot/WEB-INF/pipeline.xml”;最后将这个路径交给webx2资源加载器。
此时我们要使用PipelineService处理请求,PipelineService将会创建并初始化所有配置文件中所配置的Pipeline。在淘分享项目中,只配置了第一个名为default的Pipeline。这在/WEB-INF/webx-default.xml中可以看到。在webx2框架中,在处理使用PipelineService处理请求时,也只是取出了默认的Pipeline,即图【15】中所配置的那个Pipeline。但是处理请求的方法是handleRequest方法是propected,即是说,这个方法是webx2的默认实现,如果我们要使用自己定义的Pipeline处理请求,只需要继承WebxControllerServlet然后重写handleRequest方法即可。
接着,PipelineService使用取出来的Pipeline对请求进行处理。Pipeline代表一组顺序执行的操作,所以如果把请求代表成水,那么就很容易想到把它比作成管道。而这一组顺序执行的操作便是一系列Valve对请求的处理。
在目前淘分享的WEB-INF/pipeline.xml中只为Pipeline配置了一个Valve即: TryCatchFinallyValve。这可以在WEB-INF/pipeline.xml中看到。它有try——catch——finally结构。一般的pipeline配置都会将这个Valve作为管道的总Valve,然后在此valve内部再增加其他的Valve。TryCatchFinallyValve的作用是将一个Valve分成三个分支。说是java程序员最容易明白的是因为它和java中的try-catch-finally结构很相似,其实它的执行过程和java中的try-catch-finally结构是一模一样的(应当记住,Pipeline只是对一组顺序执行的操作的抽象,所以任何对象中包含它都是可理解的,而不是只有PipeService包含Pipeline才合理):TryPipeline,CatchPipeline,FinallyPipeline。用源码更好理解这三者是如何组合起来处理请求的。
图【17】
TryPipeline处理请求,如果处理过程中出现异常,则进入CatchPipeline处理请求,最后使用FinallyPipeline处理请求。
因为Pipeline代表一组顺序执行的操作,在WEB-INF/pipeline.xml中,在<try></try>中配置的Valve会顺序执行,只要其中一个Valve抛出异常,则表示它所在的Pipeline抛出异常。同理当TryPipeline抛出异常后,在<catch></catch>中配置的Valve会顺序执行,进行异常部分的请求处理。最后,<finally></finally>中配置的Valve会顺序执行,对请求进行最后处理。
如果WEB-INF/pipeline.xml中除了TryCatchFinallyValve还有其它Valve,那么当TryCatchFinallyValve处理完请求后,其它Valve也会接着对请求进行处理,直到最后一个Valve。
在淘单项目中只配置了一个TryCatchFinallyValve。
图【18】
(四)、webx2 Turbine
Webx Turbine建立在Webx Framework的基础上,实现了页面渲染、布局、数据验证、数据提交等一系列工作。
在webx2中home.vm被称为模板文件,在home目录下面,你会看到一个templates的目录 ,在这里面存放的是全部模板文件。这些文件分成三种:screen、control、layout。
图【19】
screen: 页面的主体
layout: 页面的布局。
control: 代表嵌在screen和layout中的页面片段
以petStroe来说明
图【20】
这个图表明,layout文件中,引用了四个control文件。screen是不能被引用的,在screen块中,显示的是screen的内容。在这里可以提前解释一下是怎么回事:screen被解析完后,screen中所有的Velocity的标签被替换成具体的内容,最终只剩下HTML标签。这个内容在webx2中就是一个字符串,它被存在一个Map结构中,在Map它的键是“screen_placeholder”,通过Velocity的语法,通过使用$screen_placeholder就可以取得此内容,所以在上图的layout文件中,screen的内容就是指$screen_placeholder的值。
我们知道,所谓动态页面就是在静态页面里面插入动态标签,在客户端请求时,将动态页面中的动态标签进行执行,替换上执行后的结果,替换完后,就是一个HTML页面。
在webx2中有control的存在就好像是JSP中有文件包含一样,即可以重复使用,也可以避免模板文件太长而使得页面不清晰等等。
例如:当我们访问http://wow.taobao.com/front/index.htm时,webx2会执行index.vm对应的Module:index.java,在index.java执行过程中,它会对找到index.vm,然后对它时行渲染,最后把渲染后的结果返回给用户。
(五)、webx2中的Module
在这里提到了Module。在webx2中,Module是基本的编程模块。不同的Module用于处理不同的逻辑,也对应于不同的模板文件。目前总共四种Module:
(1)Screen:处理页面显示逻辑的Module
(2)Control:与Screen差不多,但是可以由Screen和Layout引用的Module
(3)Layout:处理布局页面显示逻辑的Module
(4)Action:处理用户提交表单的Module
Module的架构图如下:
图【21】
(六)、模板文件与Module的关系
其实模板文件与Module是相对应的,比如screen/index.vm对应于index.java这个Module。如何来理解这个关系呢?因为我们知道,vm是动态网页,vm中不仅有HTML标签,而且还有Velocity的动态标签,因此当用户请求index.vm时,不能直接将这里面的内容返回给用户,得先将index.vm中的动态标签进行解析,并用计算的结果替换这些标签,最终形成一个HTML文档,最后再把这个HTML文档返回给用户,那么用户就能看到内容了。这也是动态网页的原理之所在,即解析动态网页,用当前的请求结果替换动态网页中的动态标签,最后形成静态网页。所谓的“动态”是指,这些有动态标签的地方的内容是不固定的。但是最终返回给用户的都是HTML网页。在webx2中,这叫对模板文件进行渲染!
这样来理解:当我们访问http://wow.taobao.com/front/index.htm时,webx2会执行index.vm对应的Module:index.java,在index.java执行过程中,它会对找到index.vm,然后对它时行渲染,最后把渲染后的结果返回给用户。
1、 PerformScreenTemplateValve
PerformScreenTemplateValve用来执行screen和模板,
图【22】
执行valve调用的是invoke方法(如下图),pelineService初始化时,Pipeline中的Valve会被加载
图【23】
PerformScreenTemplateValve主要是处理对页面的请求。它执行的前提是当前请求未被重定向。如果未重定向,则开始执行,在执行时,它首先是取得所请求的模板文件对应的Module,然后再执行该Module。举个例子,当你访问地址:http://wow.taobao.com/front/index.htm时,在PerformScreenTemplateValve中,它会首先找到index.vm所对应的Module即index.java,然后调用该类的execute(RunData)方法执行该Module。Module执行过程中,就会处理请求,然后把请求结果渲染到模板中(即将动态网页中的动态标签进行解析替换成具体的内容),形成最终的HTML文件返回给用户。找到index.vm所对应的Module是由Valve中的ModuleLoaderService来做的。ModuleLoaderService的配置如下图:
图【24】
所以当我们访问http://wow.taobao.com/front.htm时,这是在访问一个index页面,它会找到com/taobao/matrix/tandan/web/home/module/front.java这个Module。红色部分路径是模板index.vm的路径。其中红色部分中“front”是screen类型,默认情况下都是screen类型模板,所以都会去找screen目录下面的模板对应的Module,找不到则显示对应的default.vm。
2、 PerformActionValve
图【25】
用于执行业务层的action,主要是处理用户提交表单的情况。它有一个参数actionParam指明valve将根据什么参数来确定action的名称,先从请求参数中获得要访问的action的名称,然后找到这个action对应的Module,最后执行该Module。所以在vm页面提交表单时,可以使用隐藏表单(当然也可以写在Form的action属性之中),用于提交action的名字和值。
图【26】
在处理这个表单时,PerformActionValve会找到
com\taobao\matrix\taoshare\web\home\module\action\CreateShareAction,然后执行execute(RunData)方法。完成表单处理。在这里面没有页面的渲染,没有页面的返回,那么当我们处理完一个表单提交请求后,又想要给用户返回某个页面,怎么办呢?我们知道,当提交一个Form时,如果这个Form的action属性为空,则form被提交时,action将指向当前表单提交页面,这就是为什么我们平时在未指定action值的前提条件下提交表单后返回提交页面的原因。
在对表单处理完后,对于当前请求你有两种选择:(1)不对请求进行重定向(2)对请求进行重定向,重定向到另一个目标,比如另一个模板页面。如果是情况(1),那么当请求action处理完后,PerformActionValve就已经完成任务。将请求交由下一个Valve进行处理。
我们回过头看看图【25】中两个Valve的顺序,先是PerformActionValve对请求进行处理,然后是PerformScreenTemplateValve。所以当PerfromActionValve处理完请求后,就将请求将给PerformScreenTemplateValve,此时有两种情况:
(1)如果该请求未被重定向,那么PerformScreenTemplateValve继续对请求进行处理
(2)如果该请求已经被重定向,那么PerformScreenTemplateValve对请求不作任何处理
假如是第(1)种情况即请求未被重定向,那么PerformScreenTemplateValve继续对请求进行处理,但是它会去找哪个模板对应的Module呢?因为我们在页面中只是提交了一个表单,并未指明form的action属性。别忘了,当我们没有指明action属性时,那么它的值将会被默认为提交表单的页面。比如http://wow.taobao.com/owner/create_share.htm中有一个form表单,此表单中未指定action的值。如下:
<form action=”” method=”POST”>
<input type=”submit” value=”提交” />
</form>
当我们提交上面的form时,当提交时,action的值其实就是” create_share.htm的绝对路径”。所以到了PerformScreenTemplateValve处理请求时,它会找到提交表单的模板所对应的Module,并且执行该Module,在执行中渲染模板,最后输出页面展示给用户。
假如是第(2)种情况即请求被重定向,那么PerformScreenTemplateValve不会做作任何处理。要知道Action只是进行业务逻辑进行处理,并不会有页面输出,此时请求被重定向,如果要有页面输出,就只有一种可能,重定向的目标必须有页面输出。但是重定向的请求由谁来处理呢?
此时我们得再看看WEB-INF/pipeline.xml中的配置:
图【27】
在这里面,我们看到,当PerformActionValve和PerformScreenTemplateValve处理完请求后,RedirectTargetValve将会进一步处理请求,这个Valve主要是将请求进行重定向。图【27】中的goto中指明了重定向的请求将由label值为“processModule”的Valve进行处理。
它所表明的意思非常明确,因为ChooseValve是用来处理请求的,所以当请求重定向后,RedirectTargetValve没必要自己处理请求,它只用把这个请求交给ChooseValve处理即可,它所做的事情就是把重定向的请求交给负责处理请求的Valve。
在webx2中,如果调用rundata.setRedirectLocation()或rundata.setRedirectTarget()都会导致当前请求的内部重定向。从图【27】可以看出为什么是内部重定向而不是外部重定向?因为该重定向是在当前请求所处的线程中所做的,这里的重定向不过是将请求从当前线程中的一个对象(具体来说是一个Valve)交由已经处理过该请求的另一个对象(具体来说是另一个Valve)进行处理。在这里,请求还未被处理完成,并未被提交。因为Pipeline中的其它Valve还未对请求进行处理,比如pipeline.xml中<catch></catch>块之间的Valve。
(六)、webx2中模板文件的渲染
对于每一个模板而言,它都会有一个模板上下文对象TemplateContext,这个对象用于存储与模板相关的运行时数据,比如用于替换模板文件中“占位符”的具体值,也就是上面所讲的那个Map结构,其实就是TemplateContext对象。
我们现在可以理一理一个请求从被处理到处理后的数据被渲染到一个模板的过程:
当前有一个请求http://wow.taobao.com/front/index.htm,webx2取得这个请求后,开始查找到index.vm对应的Module:index.java进行请求的处理。index.java于是开始执行,在执行过程中,它会把用于替换index.vm中的“占位符”的值存储于TemplateContext中,然后将TemplateContext和index.vm模板交由模板引擎,模板引擎开始解析index.vm,然后使用TemplateContext中的具体值对index.vm中的“占位符”进行替换。最后,模板引擎返回替换后得到字符串。这个字符串仍然放在TemplateContext中,其key值为:”screen_placeholder” 这个过程其实就完成了对index.vm的渲染。但 是这个结果只是布局模板中的一部分内容。
到目前一直还未讲到用于布局的模板。在布局模板文件中,包含了网站中相对固定不变的内容(比如网站头部、导航栏、网站底部等等)和网站内容(我们请求的模板文件,如上面所说的home.vm的渲染结果)。
现在再看图【20】。这个图把一个网站分成了三个部分:上、中、下。在中间部分又分成了左、右两个部分。其中上、下以及中间的左边部分都是相对不变的。变化最多的就是Screen这块内容。对于不同请求,比如请求index.vm时,这块内容是index.vm的渲染结果;请求itemShare.vm时,这块内容就是itemShare.vm的渲染结果。
所以最终返回给用户的其实是布局模板文件的渲染结果,在这个布局模板中,包含了我们请求的模板(比如index.vm)以及网站相对不变的部分。
因为模板的渲染是由模板引擎在做,但是模板引擎如何知道我们请求的模板文件会被渲染到哪个布局模板文件中呢?其实,在index.java这个Module在处理请求时,会指定布局模板文件,像下面这样设定:
这样,index.vm的渲染结果又会用于渲染wowLayout.vm这个模板文件,最后返回给用户的即是该文件的渲染结果。
小结:对于webx2中,一个请求被处理到展示给用户的过程总共经过了两次渲染,第一次渲染是对我们请求的模板文件进行渲染,然后使用这个结果再对布局模板文件进行渲染,这是第二次渲染,这个渲染结果就是网站要展示给用户的全部内容。
(七)、webx2中的MVC模式
MVC模式中包含三部分:Controller(控制器)、Model(模型)、View(视图)
Controller:接收请求,找到适合的模型对请求处理,并找到适合的视图将处理后的数据展示给用户。
Model:业务逻辑处理。
View:视图层。
View:在webx2中,使用的就是Velocity模板文件作为视图层。
Controller:在webx2中,可以看到有两个分别很明显的Controller,它们分别是图【27】中的PerformScreenTemplateValve和PerformActionValve。这两个Valve有个共同的特点,就是接收请求,然后使用MappingService找到请求所对应的Module,最后使用该Module对请求进行处理。
Model:在淘分享项目中,Model层分成了多个层,主要有以下层次:
AO层:应用逻辑层。提供给Screen、Action等Module调用,以完成淘分享的业务实现和支持
Manager层:业务逻辑层。主要为AO层提供服务支持。在淘分享项目中,也不乏有对Manager、Service层提供支持。
DAO层:数据访问层。为Manager层提供对数据的CRUD操作。
Service层:为外部应用提供访问淘分享应用的接口层。
(八)、webx2中的Module对请求的处理
在Module中,我们写好与业务相关的处理,然后其它的事情都由框架为我们做了。所以稍微详细的学习一下Module对请求的处理是非常有好处的。Module主要有两个方向:一个是主要用于显示的TemplateModule;另一个是主要处理用户提交表单的ActionEvent。
下面分别说明这两个Module主要如何处理请求:
首要说明的是,TemplateModule和ActonEvent都是我们自己所写的Module的超类或父类,而对于TemplateModule则是使用了模板技术,所以我们从研究TemplateModule的方法执行即可学习到TemplateModule是如何处理对请求进行处理的。
在TemplateModule中,我们看到有一个execute(RunData)方法,在这里面包含了它对请求处理的全部内容。它所做的是:
(1)取得被请求模板的上下文对象TemplateContext
(2)对请求进行处理,其中处理过程中,如果一些运行时数据将被放到TemplateContext中,这些数据将会用于渲染模板
(3)取得被请求的模板文件
(4)将TemplateContext和模板文件交给模板引擎,模板引擎执行模板文件的渲染。
其中值得注意的是第(2)步,这一步在TemplateModule中是一个抽象方法execute(RunData,TemplateContext)。该方法是抽象方法表明:具体的处理是交由具体的被请求的Module来做的。从这一点可以看出其控制的痕迹非常明显,不过这种控制是通过架构方式来表达的(具体来说使用的是模板技术即Template技术)。使用该方式后,不同的请求将会找到具体的Module对请求进行处理。在具体的Module处理过程中,会使用到模型(Model)对请求进行业务逻辑处理,并把要显示的数据放入到TemplateContext中。当执行到第(3)步时,这些数据将被渲染到View(模板文件)中,最后显示给用户。
在ActionEvent中,它也是有一个execute(RunData)方法用于接收并处理请求。因为我们的Controller已经找到了具体的用于处理用户表单提交的Module,用这个Module处理时,它会默认调用Module的doPerform(RunData,TemplateContext)方法。正像Struts中一样,webx2也想在同一个Module中提供不同的doXXX(RunData,TemplateContext)方法。否则,如果一个Module中只有一个doPerform方法用于处理表单提交,那么对于每个不同的表单提交,就得创建一个新的Module来处理,这完全没有必要。所以如果一个Module中提供多个doXXX(RunData,TemplateContext)方法,那么同一个Module就可以处理多个表单提交。但是,到目前为止,我们的Controller只是为我们找到了处理请求的Module,而没有指定该Module中哪个方法来处理。所以在ActionEvent中,主要做的事情就是从请求参数中找到另一个重要参数,该参数可以写成如下形式:
(1)event_submit_do_XXX,比如event_submit_do_update
但是最终该字符串都会被转化成eventSubmitDoUpdate,所以,你也可以写成很多其它形式,比如eventSubmitDo_update,event_SubmitDo_update等等。总之这个处理算法就是会把每个单词的首字母大写,其中下划线作为单词间的分隔线。
所以我们在淘分享中可以看到在表单中有一个隐藏文本框,用于提交该参数,其中该参数的值可以是任何非空值。比如图26的表单:
Module将此参数转化成eventSubmitDoXXX后,取出“eventSubmitDo”之后的方法之后的XXX,最后调用doXXX(RunData,TemplateContext)方法处理数据。数据处理完后又是如何,在之前中已经讲过。
ps:iteye有些功能用户体验真差,比如这个编辑器就太搓啦!!!好难用!!
一、 WEBX框架的初始化
(一)、 一个请求如何进入到这个框架中来?在webx2中,它提供了一个servlet:WebxControllerServlet。这个servlet接收请求,处理请求,返回请求结果。
框架要运行前,得先初始化一些资源,资源的初始化是由servlet容器来做的。Servlet容器启动到初始化一个Servlet的过程如下:当Servlet容器(比如Jboss)启动时,它会解析web.xml文件,淘单项目中在web.xml中对WebxControllerServlet的配置如下:
图【1】
对于此配置,servlet容器会创建WebxControllerServlet对象,并且调用其抽象父类的init()方法,以完成该servlet的初始化。在AbstractWebxControllerServlet中,init()方法如下:
图【2】
图【3】
WebxLoader装载和初始化Webx中的services。在此,webxLoader读取servlet的上下文信息,然后在自身的configure()方法里初始化logging(日志)系统和service manager,如下图
图【4】
(二)、初始化日志系统
首先对日志系统进行一个说明:总体来说,webx2框架中有一个日志管理对象,框架中的所有日志事务都由该对象来处理,甚至在对WebxControllerServlet进行初始化时,也会用到它。理所当然,该对象成了WebxControllerServlet的成员变量(通过继承得到的)。
初始化过程如下:
(1) 取得日志输出的目录,即是图【1】中的以下初始化参数,这是在web.xml中对WebxControllerServlet进行配置时的初始化参数:
图【5】
当jboss启动之后,我们就可以到该目录下查看日志文件,taoshare-Debug.log,taoshare.log等等。
(2) 配置日志系统。对于日志管理对象,需要为其设置一些属性,而这些属性的配置放在/WEB-INF/log4j.xml中, 而webx2是使用WebappBootstrapResourceLoaderService对象加载资源的,理所当然,在配置log4j日志系统之前,先创建这个对象,然后再去加载log4j.xml文件并返回表示该资源的java.net.URL对象,接着解析该资源,至此就完成了日志系统的初始化。在log4J.xml中定义了日志的等级,淘分享项目中,只有大于debug等级的日志才会被打印到文件中。
图【6】
异常处理:但是在日志初始化完成之前以及日志初始化过程中出现了异常要记录一些信息,怎么办?webx2采用的方式是直接把要记录的信息输出到java的“标准”错误输出流中,如下:
图【7】
图【5】是应用程序日志输出目录,那么如果我们在日志初始化完毕之后,使用System.out.println(message)或System.err.println(message)打印消息,此时消息不会打印到dos窗口,而是打印到D:/home/admin/logs/taoshare.log文件中。这就是为什么我们在使用webx2框架时,如果在程序中打印消息,而消息输出到了taoshare.log文件中的原因。
(三)、装载webx配置文件
Webx2的Service的默认配置路径是:根目录/WEB-INF/webx.xml,这是主配置文件。此外配置文件也可放入各组件的配置文件夹下,其路径是:要目录/WEB-INF/组件名/webx.xml。
在加载webx配置文件过程中,resourceLoader首先加载默认路径下的webx.xml: /WEB-INF/webx.xml。然后加载各组件下的webx.xml:/WEB-INF/组件名/webx.xml。如下图:
图【8】
通过以上几步,webx配置文件就加载完毕了。但webx2还要加载一些默认的service,分为两部分
(1) Webx Tubine的配置文件:webx-turbine-default.xml,它是导入包自带的,
(2) Webx的默认配置文件webx-default.xml,它存放在/WEB-INF/webx-default.xml中。如图所示:
通过以上的处理之后,webx的配置文件全部加载完毕,并且这些配置文件的资源最终被抽象为一个Configuration对象,这个对象会在ServiceManager中。
(四)、初始化ServiceManager
通过(三)的配置文件加载,这些配置文件为我们提供了很多 Service,不同的Service都有不同的功能,而这些功能是框架出色完成请求处理的利器。但是如此多的Service,得对它们进行统一管理,以便在框架需要某些Service进行服务时,能通过Service的管理器获取该Service对象,让它供框架使用,比如框架在需要某个Service来提供相应功能时,但是目前框架中无可使用的该Service对象,此时Service管理器将初始化这个Service等等。Webx2就提供了一个默认的Service管理器:DefaultWebxServiceManager。
ServiceManager要管理Service,那么首先它得取得这些配置文件资源,所以在接下来的配置ServiceManger对象时,会首先将该Configuration对象交给ServiceManager。
图【10】
此外,在必要时,ServiceManager需要加载相关资源,所以它需要一个资源加载器,因此在配置ServiceManager时,webx2框架正是将(三)中用于加载webx配置文件的资源加载器交给了ServiceManager使用。
有了这些条件,初始化ServiceManager时机已经成熟。在初始化ServiceManager过程中,会做以下一些事情:
(1)通过对Configuration对象的处理,创建service名称和service配置的映射,完成之后,ServiceManager对所有的Service已经了如指掌,比如,某个Service的名字是什么,它的具体实现类是什么,初始化这个Service时应该给它初始化些什么属性等等,因为这些都已经配置在配置文件中了。
(2)初始化“资源加载服务”,这个Service在webx-default.xml中有配置,取出其中之一的配置:
二、 Webx框架的请求处理
(一)、当一个请求到达时候,容器会分析请求方法,然后调用WebxControllerServlet中对应的doXXX(HttpServletRequest,HttpServletResponse)方法。在webx2中,对于到来的请求,最终都会调用doGet()方法来处理,因为doPost()、doHead()方法都只是调用doGet()方法而已。
在doGet()方法中处理步骤如下:
1、 将请求封装为Rundata对象
webx2会将容器传入的HttpServletRequest和HttpServletResponse进行封装,将二者封装到Webx2提供的RunData对象中(封装由RunDataService进行处理)
为什么要将请封装成RunData?
封装之后在webx中就可以直接使用rundata来取参数 ,甚至在rundata中为请求做缓存。这样的封装可以更加个性化的使用request和response,根据不同的需要,可以在rundata中提供不同的方法,来满足我们对request和response的处理。比如rundata.getInt("param");这个方法在request中是没有的,这是rundata提供的。这样我们取参数就更加方便了。如果使用request,我们将使用request.getParameter("param");然后再把取得的值转成我们想要的类型
图【12】
RunDataService这个Service在webx-default.xml中配置如下
图【13】
2、 为每个请求保存一个RunData对象
得到RunData之后,在处理请求之前,webx2又对该RunData进行了处理。此时它将RunData放入到ThreadContextService这个Service之中。
图【14】
先构建请求再执行doGet()方法,
(三)、PipelineService处理请求
通过以上的准备工作,现在开始处理请求,因为请求是被封装在RunData里的,所以处理的结果也存放在RunData对象中。请求的处理是由PiplelineService完成PiplelineService Service配置在/WEB-INF/webx-default.xml中。
图【15】
PipelineService在初始化时,是使用ResourceLoaderService来加载资源的,对于这个service,在前面已经讲述了它的初始化。ResourceLoaderService所做的事情就仅仅是找到资源路径,然后交给底层的webx2资源加载器,完成资源的加载。那ResourceLoaderService是如何找到资源的呢?其实ResourceLoaderService提供了查找资源的功能,它提供这个功能的原因在于webx2框架开发者想让开发者可以将一些些复杂的资源路径与一个简短的资源路径进行映射。我们先来看ResourceLoaderService的配置,它的配置在webx-default.xml中可以找到,如下图:
resources.xml文件便是它使用的映射文件,来看看resources.xml文件中的部分内容:
图【16】
红框中的内容的意思就是,使用“/taoshare”这个路径代替复杂路径:“/webroot/WEB-INF”。
现在我们理清了PipelineService初始化时是如何找到资源的。PipelineService初始化时,会使用ResourceLoaderService去加载“/taoshare/pipeline.xml”资源。ResourceLoaderService在加载此资源时,它会首先解析出路径中的映射路径和资源名,即它会依据映射文件resources.xml解析出此路径中的映射路径部分:“/taoshare”和资源名:“pipeline.xml”;接着,它从映射文件中找到“/taoshare”所对应的实际资源路径:“/webroot/WEB-INF/”;接着将此实际资源路径与资源名组合得到资源的真正路径:“/webroot/WEB-INF/pipeline.xml”;最后将这个路径交给webx2资源加载器。
此时我们要使用PipelineService处理请求,PipelineService将会创建并初始化所有配置文件中所配置的Pipeline。在淘分享项目中,只配置了第一个名为default的Pipeline。这在/WEB-INF/webx-default.xml中可以看到。在webx2框架中,在处理使用PipelineService处理请求时,也只是取出了默认的Pipeline,即图【15】中所配置的那个Pipeline。但是处理请求的方法是handleRequest方法是propected,即是说,这个方法是webx2的默认实现,如果我们要使用自己定义的Pipeline处理请求,只需要继承WebxControllerServlet然后重写handleRequest方法即可。
接着,PipelineService使用取出来的Pipeline对请求进行处理。Pipeline代表一组顺序执行的操作,所以如果把请求代表成水,那么就很容易想到把它比作成管道。而这一组顺序执行的操作便是一系列Valve对请求的处理。
在目前淘分享的WEB-INF/pipeline.xml中只为Pipeline配置了一个Valve即: TryCatchFinallyValve。这可以在WEB-INF/pipeline.xml中看到。它有try——catch——finally结构。一般的pipeline配置都会将这个Valve作为管道的总Valve,然后在此valve内部再增加其他的Valve。TryCatchFinallyValve的作用是将一个Valve分成三个分支。说是java程序员最容易明白的是因为它和java中的try-catch-finally结构很相似,其实它的执行过程和java中的try-catch-finally结构是一模一样的(应当记住,Pipeline只是对一组顺序执行的操作的抽象,所以任何对象中包含它都是可理解的,而不是只有PipeService包含Pipeline才合理):TryPipeline,CatchPipeline,FinallyPipeline。用源码更好理解这三者是如何组合起来处理请求的。
图【17】
TryPipeline处理请求,如果处理过程中出现异常,则进入CatchPipeline处理请求,最后使用FinallyPipeline处理请求。
因为Pipeline代表一组顺序执行的操作,在WEB-INF/pipeline.xml中,在<try></try>中配置的Valve会顺序执行,只要其中一个Valve抛出异常,则表示它所在的Pipeline抛出异常。同理当TryPipeline抛出异常后,在<catch></catch>中配置的Valve会顺序执行,进行异常部分的请求处理。最后,<finally></finally>中配置的Valve会顺序执行,对请求进行最后处理。
如果WEB-INF/pipeline.xml中除了TryCatchFinallyValve还有其它Valve,那么当TryCatchFinallyValve处理完请求后,其它Valve也会接着对请求进行处理,直到最后一个Valve。
在淘单项目中只配置了一个TryCatchFinallyValve。
图【18】
(四)、webx2 Turbine
Webx Turbine建立在Webx Framework的基础上,实现了页面渲染、布局、数据验证、数据提交等一系列工作。
在webx2中home.vm被称为模板文件,在home目录下面,你会看到一个templates的目录 ,在这里面存放的是全部模板文件。这些文件分成三种:screen、control、layout。
图【19】
screen: 页面的主体
layout: 页面的布局。
control: 代表嵌在screen和layout中的页面片段
以petStroe来说明
图【20】
这个图表明,layout文件中,引用了四个control文件。screen是不能被引用的,在screen块中,显示的是screen的内容。在这里可以提前解释一下是怎么回事:screen被解析完后,screen中所有的Velocity的标签被替换成具体的内容,最终只剩下HTML标签。这个内容在webx2中就是一个字符串,它被存在一个Map结构中,在Map它的键是“screen_placeholder”,通过Velocity的语法,通过使用$screen_placeholder就可以取得此内容,所以在上图的layout文件中,screen的内容就是指$screen_placeholder的值。
我们知道,所谓动态页面就是在静态页面里面插入动态标签,在客户端请求时,将动态页面中的动态标签进行执行,替换上执行后的结果,替换完后,就是一个HTML页面。
在webx2中有control的存在就好像是JSP中有文件包含一样,即可以重复使用,也可以避免模板文件太长而使得页面不清晰等等。
例如:当我们访问http://wow.taobao.com/front/index.htm时,webx2会执行index.vm对应的Module:index.java,在index.java执行过程中,它会对找到index.vm,然后对它时行渲染,最后把渲染后的结果返回给用户。
(五)、webx2中的Module
在这里提到了Module。在webx2中,Module是基本的编程模块。不同的Module用于处理不同的逻辑,也对应于不同的模板文件。目前总共四种Module:
(1)Screen:处理页面显示逻辑的Module
(2)Control:与Screen差不多,但是可以由Screen和Layout引用的Module
(3)Layout:处理布局页面显示逻辑的Module
(4)Action:处理用户提交表单的Module
Module的架构图如下:
图【21】
(六)、模板文件与Module的关系
其实模板文件与Module是相对应的,比如screen/index.vm对应于index.java这个Module。如何来理解这个关系呢?因为我们知道,vm是动态网页,vm中不仅有HTML标签,而且还有Velocity的动态标签,因此当用户请求index.vm时,不能直接将这里面的内容返回给用户,得先将index.vm中的动态标签进行解析,并用计算的结果替换这些标签,最终形成一个HTML文档,最后再把这个HTML文档返回给用户,那么用户就能看到内容了。这也是动态网页的原理之所在,即解析动态网页,用当前的请求结果替换动态网页中的动态标签,最后形成静态网页。所谓的“动态”是指,这些有动态标签的地方的内容是不固定的。但是最终返回给用户的都是HTML网页。在webx2中,这叫对模板文件进行渲染!
这样来理解:当我们访问http://wow.taobao.com/front/index.htm时,webx2会执行index.vm对应的Module:index.java,在index.java执行过程中,它会对找到index.vm,然后对它时行渲染,最后把渲染后的结果返回给用户。
1、 PerformScreenTemplateValve
PerformScreenTemplateValve用来执行screen和模板,
图【22】
执行valve调用的是invoke方法(如下图),pelineService初始化时,Pipeline中的Valve会被加载
图【23】
PerformScreenTemplateValve主要是处理对页面的请求。它执行的前提是当前请求未被重定向。如果未重定向,则开始执行,在执行时,它首先是取得所请求的模板文件对应的Module,然后再执行该Module。举个例子,当你访问地址:http://wow.taobao.com/front/index.htm时,在PerformScreenTemplateValve中,它会首先找到index.vm所对应的Module即index.java,然后调用该类的execute(RunData)方法执行该Module。Module执行过程中,就会处理请求,然后把请求结果渲染到模板中(即将动态网页中的动态标签进行解析替换成具体的内容),形成最终的HTML文件返回给用户。找到index.vm所对应的Module是由Valve中的ModuleLoaderService来做的。ModuleLoaderService的配置如下图:
图【24】
所以当我们访问http://wow.taobao.com/front.htm时,这是在访问一个index页面,它会找到com/taobao/matrix/tandan/web/home/module/front.java这个Module。红色部分路径是模板index.vm的路径。其中红色部分中“front”是screen类型,默认情况下都是screen类型模板,所以都会去找screen目录下面的模板对应的Module,找不到则显示对应的default.vm。
2、 PerformActionValve
图【25】
用于执行业务层的action,主要是处理用户提交表单的情况。它有一个参数actionParam指明valve将根据什么参数来确定action的名称,先从请求参数中获得要访问的action的名称,然后找到这个action对应的Module,最后执行该Module。所以在vm页面提交表单时,可以使用隐藏表单(当然也可以写在Form的action属性之中),用于提交action的名字和值。
图【26】
在处理这个表单时,PerformActionValve会找到
com\taobao\matrix\taoshare\web\home\module\action\CreateShareAction,然后执行execute(RunData)方法。完成表单处理。在这里面没有页面的渲染,没有页面的返回,那么当我们处理完一个表单提交请求后,又想要给用户返回某个页面,怎么办呢?我们知道,当提交一个Form时,如果这个Form的action属性为空,则form被提交时,action将指向当前表单提交页面,这就是为什么我们平时在未指定action值的前提条件下提交表单后返回提交页面的原因。
在对表单处理完后,对于当前请求你有两种选择:(1)不对请求进行重定向(2)对请求进行重定向,重定向到另一个目标,比如另一个模板页面。如果是情况(1),那么当请求action处理完后,PerformActionValve就已经完成任务。将请求交由下一个Valve进行处理。
我们回过头看看图【25】中两个Valve的顺序,先是PerformActionValve对请求进行处理,然后是PerformScreenTemplateValve。所以当PerfromActionValve处理完请求后,就将请求将给PerformScreenTemplateValve,此时有两种情况:
(1)如果该请求未被重定向,那么PerformScreenTemplateValve继续对请求进行处理
(2)如果该请求已经被重定向,那么PerformScreenTemplateValve对请求不作任何处理
假如是第(1)种情况即请求未被重定向,那么PerformScreenTemplateValve继续对请求进行处理,但是它会去找哪个模板对应的Module呢?因为我们在页面中只是提交了一个表单,并未指明form的action属性。别忘了,当我们没有指明action属性时,那么它的值将会被默认为提交表单的页面。比如http://wow.taobao.com/owner/create_share.htm中有一个form表单,此表单中未指定action的值。如下:
<form action=”” method=”POST”>
<input type=”submit” value=”提交” />
</form>
当我们提交上面的form时,当提交时,action的值其实就是” create_share.htm的绝对路径”。所以到了PerformScreenTemplateValve处理请求时,它会找到提交表单的模板所对应的Module,并且执行该Module,在执行中渲染模板,最后输出页面展示给用户。
假如是第(2)种情况即请求被重定向,那么PerformScreenTemplateValve不会做作任何处理。要知道Action只是进行业务逻辑进行处理,并不会有页面输出,此时请求被重定向,如果要有页面输出,就只有一种可能,重定向的目标必须有页面输出。但是重定向的请求由谁来处理呢?
此时我们得再看看WEB-INF/pipeline.xml中的配置:
图【27】
在这里面,我们看到,当PerformActionValve和PerformScreenTemplateValve处理完请求后,RedirectTargetValve将会进一步处理请求,这个Valve主要是将请求进行重定向。图【27】中的goto中指明了重定向的请求将由label值为“processModule”的Valve进行处理。
它所表明的意思非常明确,因为ChooseValve是用来处理请求的,所以当请求重定向后,RedirectTargetValve没必要自己处理请求,它只用把这个请求交给ChooseValve处理即可,它所做的事情就是把重定向的请求交给负责处理请求的Valve。
在webx2中,如果调用rundata.setRedirectLocation()或rundata.setRedirectTarget()都会导致当前请求的内部重定向。从图【27】可以看出为什么是内部重定向而不是外部重定向?因为该重定向是在当前请求所处的线程中所做的,这里的重定向不过是将请求从当前线程中的一个对象(具体来说是一个Valve)交由已经处理过该请求的另一个对象(具体来说是另一个Valve)进行处理。在这里,请求还未被处理完成,并未被提交。因为Pipeline中的其它Valve还未对请求进行处理,比如pipeline.xml中<catch></catch>块之间的Valve。
(六)、webx2中模板文件的渲染
对于每一个模板而言,它都会有一个模板上下文对象TemplateContext,这个对象用于存储与模板相关的运行时数据,比如用于替换模板文件中“占位符”的具体值,也就是上面所讲的那个Map结构,其实就是TemplateContext对象。
我们现在可以理一理一个请求从被处理到处理后的数据被渲染到一个模板的过程:
当前有一个请求http://wow.taobao.com/front/index.htm,webx2取得这个请求后,开始查找到index.vm对应的Module:index.java进行请求的处理。index.java于是开始执行,在执行过程中,它会把用于替换index.vm中的“占位符”的值存储于TemplateContext中,然后将TemplateContext和index.vm模板交由模板引擎,模板引擎开始解析index.vm,然后使用TemplateContext中的具体值对index.vm中的“占位符”进行替换。最后,模板引擎返回替换后得到字符串。这个字符串仍然放在TemplateContext中,其key值为:”screen_placeholder” 这个过程其实就完成了对index.vm的渲染。但 是这个结果只是布局模板中的一部分内容。
到目前一直还未讲到用于布局的模板。在布局模板文件中,包含了网站中相对固定不变的内容(比如网站头部、导航栏、网站底部等等)和网站内容(我们请求的模板文件,如上面所说的home.vm的渲染结果)。
现在再看图【20】。这个图把一个网站分成了三个部分:上、中、下。在中间部分又分成了左、右两个部分。其中上、下以及中间的左边部分都是相对不变的。变化最多的就是Screen这块内容。对于不同请求,比如请求index.vm时,这块内容是index.vm的渲染结果;请求itemShare.vm时,这块内容就是itemShare.vm的渲染结果。
所以最终返回给用户的其实是布局模板文件的渲染结果,在这个布局模板中,包含了我们请求的模板(比如index.vm)以及网站相对不变的部分。
因为模板的渲染是由模板引擎在做,但是模板引擎如何知道我们请求的模板文件会被渲染到哪个布局模板文件中呢?其实,在index.java这个Module在处理请求时,会指定布局模板文件,像下面这样设定:
rundata.getModuleInfo().setLayoutTemplate("emptyLayout.vm");
这样,index.vm的渲染结果又会用于渲染wowLayout.vm这个模板文件,最后返回给用户的即是该文件的渲染结果。
小结:对于webx2中,一个请求被处理到展示给用户的过程总共经过了两次渲染,第一次渲染是对我们请求的模板文件进行渲染,然后使用这个结果再对布局模板文件进行渲染,这是第二次渲染,这个渲染结果就是网站要展示给用户的全部内容。
(七)、webx2中的MVC模式
MVC模式中包含三部分:Controller(控制器)、Model(模型)、View(视图)
Controller:接收请求,找到适合的模型对请求处理,并找到适合的视图将处理后的数据展示给用户。
Model:业务逻辑处理。
View:视图层。
View:在webx2中,使用的就是Velocity模板文件作为视图层。
Controller:在webx2中,可以看到有两个分别很明显的Controller,它们分别是图【27】中的PerformScreenTemplateValve和PerformActionValve。这两个Valve有个共同的特点,就是接收请求,然后使用MappingService找到请求所对应的Module,最后使用该Module对请求进行处理。
Model:在淘分享项目中,Model层分成了多个层,主要有以下层次:
AO层:应用逻辑层。提供给Screen、Action等Module调用,以完成淘分享的业务实现和支持
Manager层:业务逻辑层。主要为AO层提供服务支持。在淘分享项目中,也不乏有对Manager、Service层提供支持。
DAO层:数据访问层。为Manager层提供对数据的CRUD操作。
Service层:为外部应用提供访问淘分享应用的接口层。
(八)、webx2中的Module对请求的处理
在Module中,我们写好与业务相关的处理,然后其它的事情都由框架为我们做了。所以稍微详细的学习一下Module对请求的处理是非常有好处的。Module主要有两个方向:一个是主要用于显示的TemplateModule;另一个是主要处理用户提交表单的ActionEvent。
下面分别说明这两个Module主要如何处理请求:
首要说明的是,TemplateModule和ActonEvent都是我们自己所写的Module的超类或父类,而对于TemplateModule则是使用了模板技术,所以我们从研究TemplateModule的方法执行即可学习到TemplateModule是如何处理对请求进行处理的。
在TemplateModule中,我们看到有一个execute(RunData)方法,在这里面包含了它对请求处理的全部内容。它所做的是:
(1)取得被请求模板的上下文对象TemplateContext
(2)对请求进行处理,其中处理过程中,如果一些运行时数据将被放到TemplateContext中,这些数据将会用于渲染模板
(3)取得被请求的模板文件
(4)将TemplateContext和模板文件交给模板引擎,模板引擎执行模板文件的渲染。
其中值得注意的是第(2)步,这一步在TemplateModule中是一个抽象方法execute(RunData,TemplateContext)。该方法是抽象方法表明:具体的处理是交由具体的被请求的Module来做的。从这一点可以看出其控制的痕迹非常明显,不过这种控制是通过架构方式来表达的(具体来说使用的是模板技术即Template技术)。使用该方式后,不同的请求将会找到具体的Module对请求进行处理。在具体的Module处理过程中,会使用到模型(Model)对请求进行业务逻辑处理,并把要显示的数据放入到TemplateContext中。当执行到第(3)步时,这些数据将被渲染到View(模板文件)中,最后显示给用户。
在ActionEvent中,它也是有一个execute(RunData)方法用于接收并处理请求。因为我们的Controller已经找到了具体的用于处理用户表单提交的Module,用这个Module处理时,它会默认调用Module的doPerform(RunData,TemplateContext)方法。正像Struts中一样,webx2也想在同一个Module中提供不同的doXXX(RunData,TemplateContext)方法。否则,如果一个Module中只有一个doPerform方法用于处理表单提交,那么对于每个不同的表单提交,就得创建一个新的Module来处理,这完全没有必要。所以如果一个Module中提供多个doXXX(RunData,TemplateContext)方法,那么同一个Module就可以处理多个表单提交。但是,到目前为止,我们的Controller只是为我们找到了处理请求的Module,而没有指定该Module中哪个方法来处理。所以在ActionEvent中,主要做的事情就是从请求参数中找到另一个重要参数,该参数可以写成如下形式:
(1)event_submit_do_XXX,比如event_submit_do_update
但是最终该字符串都会被转化成eventSubmitDoUpdate,所以,你也可以写成很多其它形式,比如eventSubmitDo_update,event_SubmitDo_update等等。总之这个处理算法就是会把每个单词的首字母大写,其中下划线作为单词间的分隔线。
所以我们在淘分享中可以看到在表单中有一个隐藏文本框,用于提交该参数,其中该参数的值可以是任何非空值。比如图26的表单:
Module将此参数转化成eventSubmitDoXXX后,取出“eventSubmitDo”之后的方法之后的XXX,最后调用doXXX(RunData,TemplateContext)方法处理数据。数据处理完后又是如何,在之前中已经讲过。
ps:iteye有些功能用户体验真差,比如这个编辑器就太搓啦!!!好难用!!
评论
5 楼
jayzc1234
2017-03-14
讲的很好 看头像还是个女的 真是牛逼
4 楼
donaldfischer
2015-04-24
膜拜博主。。。分析的很透彻... 特意登录回复 ! 谢谢分享
3 楼
秦时明月黑
2014-09-01
写的非常详细,用心的牛人
2 楼
顽固的卡夫卡
2012-08-26
开源文档里有一段话:
“2010年,Webx 3.0发布。Webx 3.0抛弃了Webx 2.0中过时的、从Turbine中发展而来的Service框架,直接采用Spring作为其基础,并对Spring作了重大改进。Webx 3.0完全兼容Webx 2.0的代码,只需要修改配置文件就可完成升级。”
据我了解,webx3中很多概念与webx2都不同。在找资料的同时会混乱两个版本的某些概念。
“2010年,Webx 3.0发布。Webx 3.0抛弃了Webx 2.0中过时的、从Turbine中发展而来的Service框架,直接采用Spring作为其基础,并对Spring作了重大改进。Webx 3.0完全兼容Webx 2.0的代码,只需要修改配置文件就可完成升级。”
据我了解,webx3中很多概念与webx2都不同。在找资料的同时会混乱两个版本的某些概念。
1 楼
顽固的卡夫卡
2012-08-26
除了webx开源文档,不知有无其他资料?我是新小二,正在入门webx。
发表评论
-
jetty启动以及嵌入式启动
2013-08-18 21:47 25186首先得下载jetty http:/ ... -
iBATIS 对 SQL 语句的解析过程
2013-07-01 22:32 2958总体来说 iBATIS 的系统 ... -
hive原理(未完。。)
2013-01-06 22:46 1983hive就是一个将hiveql(其实是sql的子集或者说一点点 ... -
搜索切换dump之MapReduce讲解
2012-12-23 20:16 1586分享聚合dump的是评价的 ... -
开启mapReduce
2012-12-18 10:53 1131用最简短的语言解释MapReduce: We wa ... -
Webx之表单验证
2011-12-29 10:55 1977引入服务器端表单验证service,是通过在webx.xml中 ...
相关推荐
【标题】:Webx框架详解及学习总结 【描述】:本文主要介绍Webx框架的概览、核心架构以及环境搭建过程,旨在帮助读者理解和掌握Webx作为一个MVC框架的关键特性。 【正文】: Webx是一个基于MVC(Model-View-...
【标题】:“Webx 总结” 在IT领域,Webx是一个基于Java的开源Web应用框架,主要用于构建企业级的...通过学习这些内容,开发者不仅可以掌握Webx的基本使用,还能深入了解其工作机制,从而在实际开发中更加得心应手。
该文档为官方webx框架文档,对webx进行了全面的讲解,非常实用,并附学习的Demo 为什么要用Webx而不是其它的开源框架? 现在有很多Java的Web框架可供选择,并且它们也都是免费的。例如: • Struts • Webwork • ...
llerServlet 是 Webx 框架的核心组件,它负责处理所有以 .htm 和 .do 结尾的请求。...对于初学者来说,理解 Webx 的核心概念,如 ControllerServlet 和服务配置,是进一步学习和使用 Webx 的关键。
通过学习这些材料,开发者不仅可以了解Webx的运作原理,还能掌握如何在实际项目中有效利用这一工具。 此外,阿里巴巴还推出了一系列其他的Web框架,如Spring Boot的增强版Spring Cloud Alibaba,它提供了微服务治理...
#### 六、总结 淘宝技术架构的发展历程不仅反映了中国互联网行业的快速发展,也为我们提供了宝贵的经验和启示: - **持续创新**:面对业务发展的挑战,不断尝试新技术、新方法。 - **灵活适应**:根据实际情况调整...
总结起来,这个教育网网站群设计方案关注了服务器配置、网站架构、安全性、用户体验和内容管理等多个核心要素,旨在构建一个高效、安全、易管理的教育信息平台。同时,它还融入了Web 2.0的理念,力求提供丰富、互动...