74 *包括对所有事件的触发、执行,对Handler的调用等。后文会有专门的小节进行介绍。*/ 75 handler2.BeginProcessRequest(extraData, this._handlerCompletionCallback, extraData); 76 } 77 else 78 { 79 applicationInstance.ProcessRequest(extraData); 80 this.FinishRequest(extraData.WorkerRequest, extraData, null); 81 } 82 } 83 catch (Exception exception) 84 { 85 extraData.Response.InitResponseWriter(); 86 this.FinishRequest(wr, extraData, exception); 87 } 88}
从上面的代码可以看出,HttpApplication对象是经由HttpApplicationFactory.GetApplicationInstance(并最终调用HttpRuntime.CreateNonPublicInstance)创建的。 HttpApplicationFactory对象会先解译目录中的Global.asax文件,接着加载虚拟目录内的Application Assembly(Global.dll),而后合并两者创建出一个Ghost Application Class,最后编译此Class后取得对象实例后返回至HttpRuntime对象,这个对象实例就是HttpApplication对象。解译与编译.asax文件的动作只发生于此虚拟目录第一次处理用户要求,或是Global.asax、Global.dll在前次执行后又做了变动时。至于HttpRuntime.CreateNonPublicInstance方法内部到底是通过什么样的代码,先后调用了Parser和Compiler,根据machine.config和web.config来生成一个HttpApplication对象,笔者未能通过自己的努力追踪到,盼高手补充。
总之,通过machine.config及web.config档中对<httpHandlers>节点的说明,HttpApplication可以从HanlerFactory的缓存池HandlerFactoryCache中,根据当前请求的页面类型名称,获得所需的HandlerFactory。对于我们通常的aspx页面,返回的将是PageHandlerFactory(HttpApplication通过GetFactory()方法返回它),而PageHandlerFactory将接下来完成对所请求页面的Parser,并返回一个代表所请求页面的Page类: Page page = BuildManager.CreateInstanceFromVirtualPath(virtualPath, typeof(Page), context, true, true) as Page; 上面这句代码是被PageHandleFactory的GetHandle方法间接调用的(大家可以从HttpApplication.MapHttpHandler方法),调用返回的page对象是一个非常非常关键的实例(具体的方法调用过程中应当包括了对PageParser和PageBuilder的调用,望高手补充),因为它就是我们普通的aspx页面处理流程中,那个扮演着IHttpHandler的角色!也正因为此,我们在HttpRuntime.ProcessRequestInternal()方法里看到的applicationInstance.ProcessRequest(extraData)调用,实际上是调用的一个System.Web.UI.Page类型实例的ProcessRequest方法,整个执行流也因此进入了Page.ProcessRequestMain()的里面。我们平常所说的一个页面的生命周期的若干事件,您只要好好看看这个方法的实现,就都能明白了。由于这个方法估计是大家平时看的比较多的,对之也比较熟悉,本文这里就不多解释了。
四.HttpApplication的事件机制
到上面介绍的内容为止,整个处理流程基本上就讲完了。但如果只介绍到这里,恐怕大家对HttpApplication, IHttpModule, IHttpHandler三者的关系还是不太清楚。一个请求过程的所有事件(如BeginRequest、AuthenticateRequest等)是如何被触发的?如何通过自己的自定义Module来处理这些事件?Handler的处理又是在什么位置切入的?其实,这一切都是以HttpApplication内置的事件机制为核心的,下面就让我们来一步步揭示出它的实现方式(对.net中事件机制不够熟悉的读者可以先参看笔者另一篇文章:Part I of Events in Asp.Net: Events in .Net)。这里面涉及的事件很多,我们就以BeginRequest这个事件为例来说明: (1)首先当然是要对事件本身进行定义 public event EventHandler BeginRequest (2)由于有不止一个事件,为了方便对所有事件的管理,给每一个事件定义了一个唯一key,用作在事件容器中查找指定事件的标志 private static readonly object EventBeginRequest; (3)把对一个个事件触发定义为一个个“执行步骤的执行”,下面是对“执行步骤”这个接口的定义 internal interface IExecutionStep { // 每一个“执行步骤”的执行命令 void Execute(); // Properties bool CompletedSynchronously { get; } bool IsCancellable { get; } }
这里为什么要看似多此一举把对事件的触发再做一层封装,封装为所谓“执行步骤”,在后文介绍。 (4)用一个数组来保存所有的执行步骤。可以想象,真正到执行的时候,从数组里取出每一项IExecutionStep,再执行其Execute()方法即可。而Execute里面,肯定是对事件委托链的调用无疑 private IExecutionStep[] _execSteps; (5)HttpModule在它初始化的时候完成对事件的注册。在创建HttpApplication对象的时候,会调用这个对象的InitInternal方法,这个方法内部会调用InitModules() ,它用来初始化和这个应用有关的所有HttpModule,而在这个方法里面,最重要的就是调用了每个Module的Init()方法。那么,如果我们有一个自定义的HttpModule,并希望这个Module去响应BeginRequest事件,我们应该这样定义自己的Module的Init()方法: public void Init(HttpApplication application) { application.BeginRequest += new EventHandler(this.YourCustomMethodForBeginRequestEvent); }
这样就完成了注册。当然,此时事件还没有执行,你也还没有看到事件和执行步骤的关系,HttpHandler也尚未登场。
(6)前面提到过所谓“执行步骤”,让我们先来看两个在HttpApplication中被定义的IExecutionStep,因为这两个执行步骤中所完成的,一个是对HttpHandler的解析和实例化,一个是调用HttpHandler的ProcessRequest方法,他们分别是MapHandlerExecutionStep和CallHandlerExecutionStep。读者可以自己去读一下这两个类的Execute()方法的代码,当您第一次从CallHandlerExecutionStep.Execute()中看到handler.ProcessRequest(context)和handler2.BeginProcessRequest(context, this._completionCallback, null)这样的调用语句,从MapHandlerExecutionStep.Execute()中看到context.Handler = this._application.MapHttpHandler(context, request.RequestType, request.FilePathObject, request.PhysicalPathInternal, false); 这样的调用语句,一定会有一种“哎呀,原来你们在这里”的快感,呵呵。
(7)对“执行步骤数组”的初始化操作,就放在HttpApplication.InitInternal()方法里,具体就是下面这段语句:
执行步骤的初始化 1this.CreateEventExecutionSteps(EventBeginRequest, steps); 2this.CreateEventExecutionSteps(EventAuthenticateRequest, steps); 3this.CreateEventExecutionSteps(EventDefaultAuthentication, steps); 4this.CreateEventExecutionSteps(EventPostAuthenticateRequest, steps); 5this.CreateEventExecutionSteps(EventAuthorizeRequest, steps); 6this.CreateEventExecutionSteps(EventPostAuthorizeRequest, steps); 7this.CreateEventExecutionSteps(EventResolveRequestCache, steps); 8this.CreateEventExecutionSteps(EventPostResolveRequestCache, steps); 9steps.Add(new MapHandlerExecutionStep(this)); 10this.CreateEventExecutionSteps(EventPostMapRequestHandler, steps); 11this.CreateEventExecutionSteps(EventAcquireRequestState, steps); 12this.CreateEventExecutionSteps(EventPostAcquireRequestState, steps); 13this.CreateEventExecutionSteps(EventPreRequestHandlerExecute, steps); 14steps.Add(new CallHandlerExecutionStep(this)); //从这里,您可以很容易看到Handler对页面的解析处理在整个请求的处理的什么位置 15 //有哪些应用程序事件在它之前,哪些在它之后 16this.CreateEventExecutionSteps(EventPostRequestHandlerExecute, steps); 17this.CreateEventExecutionSteps(EventReleaseRequestState, steps); 18this.CreateEventExecutionSteps(EventPostReleaseRequestState, steps); 19steps.Add(new CallFilterExecutionStep(this)); 20this.CreateEventExecutionSteps(EventUpdateRequestCache, steps); 21this.CreateEventExecutionSteps(EventPostUpdateRequestCache, steps); 22this._endRequestStepIndex = steps.Count; 23this.CreateEventExecutionSteps(EventEndRequest, steps); 24steps.Add(new NoopExecutionStep()); 25this._execSteps = new IExecutionStep[steps.Count]; 26steps.CopyTo(this._execSteps); //把整个数组拷贝给HttpApplication的私有变量_execSteps可以看到,对执行步骤数组的初始化有两种形式,一种是直接添加一个步骤:
steps.Add(new MapHandlerExecutionStep(this));
这种执行步骤是和事件无关的,只是在整个事件流中的特定位置执行一些特定的操作(实例化Handler之类的)。
而另一种是要把相关事件的处理方法列表添加到步骤中,每一个步骤其实是对一个事件的处理:
this.CreateEventExecutionSteps(EventBeginRequest, steps);
(8)执行步骤数组的执行。
其实在前文中已经提到过了,整个执行步骤数组的执行,是在HttpApplication.ResumeSteps()中调用的。恐怕就算不看这个方法的代码,大家也能想象得出,它是遍历整个执行步骤数组,然后调用其中每一项的Execute方法。这里大家大概也就清楚了,为什么会有一个执行步骤的概念。以笔者看来,首先它在概念上很好理解,完全贴合整个应用处理的管道模型pipeline;第二它屏蔽了引发事件的应用执行步骤和普通的内置执行步骤之间的差别;第三是比较容易在以后对整个流程进行改进和扩展。
(编辑:aniston)
|