从.Net类库代码来看Asp.net运行时(转自酷网动力)

 写在前面的话:网上讲Asp.net运行模式的好文章已经很多了,笔者本不用多此一举,另成一文。但从笔者自己的学习经验看,如果学到的这些知识不能对应 到类库中的源代码,印象总归不够深刻,大有隔靴搔痒之感。只好自己写上一篇,对这方面的知识做个小小的总结。文中所有内容都是笔者在看了网上很多文章后, 结合自己的开发经验得出的一些理解,难免有错误的地方,欢迎批评指出。另外,由于笔者能力所限,很多地方并未说透(真正对应到代码),也盼高手能够给予补 充。
  
  一.进入Asp.net运行时之前
   虽然本文的重点是对托管代码的解析,但为了整个知识点的完整性,这里简单介绍一下IIS处理请求的一些基本情况。在一个IIS服务器上,你可以设置多个应 用程序池(每个应用程序池可以单独设置允许使用的最大内存数量、CPU使用率、回收工作进程的时间间隔等参数,而且一个应用程序池里面只能使用一个版本 的.NET Framework),然后把自己的Web应用分别部署到这些应用程序池中。在默认情况下,每个应用池会有一个工作进程w3wp.exe来维护(如果开通 了Web园功能,也可以设置多个工作进程)。每个应用程序(虚拟目录)在池中都有自己的应用程序域,这些应用程序域都处于这个应用程序池的工作进程的进程 空间内。
   IIS是通过各种ISAPI的扩展来处理各种类型的应用的。当我们从客户端提交一个请求过来之后,IIS会根据请求的页面或者服务的类型,把请求映射到指 定的ISAPI扩展。比方说,如果我们需要让IIS支持perl这样的服务器端程序(当然,这个移植工作早就有人做过了),我们就需要编写一个专门处理对 perl页面进行的请求的ISAPI扩展。根据ISAPI的定义(符合这个定义的ISAPI扩展才能和IIS正常交互),在你的扩展中可以包括ISAPI Extension和ISAPI Filter两大部分。ISAPI Extension是对请求的处理程序,完成和web服务器之间的输入输出;而ISAPI Filter则是一些回调接口,你可以通过实现这些接口来介入到整个请求处理的每一步骤,对Authentication,RevolveCache等环 节进行控制。另外,ISAPI本身就是在工作进程里运行的,而asp.net运行时也是在工作进程里运行的,所以两者的交互非常有效率。
   对于.aspx页面,这个扩展就是aspnet_isapi.dll。因为这些ISAPI都是非托管的Win32应用,直接对它们进行改动是比较困难的。 所以,为了增强Asp.net运行时的可扩展性,aspnet_isapi.dll本身的功能非常少,我们可以把aspnet_isapi.dll简单理 解为请求信息的路由器,负责把请求从IIS传送到asp.net运行时。而后面我们将要讲到的HttpHandle和HttpModule则分别担负起了 ISAPI Extension和ISAPI Filter的功能,幸运的是,HttpHandle和HttpModule可以由纯的托管代码来实现。
  
  二.从非托管代码到托管代码
   前面说了,aspnet_isapi.dll是非托管代码,而asp.net运行时是托管代码,他们都运行在w3wp.exe工作进程里面,那么两者之间 的调用点发生在什么地方呢?在介绍接下来的内容之前必须先介绍一个概念:ECB。ECB的全称是Extension Control Block,它是一个非托管资源包,具有对ISAPI接口完整的访问能力,包含了所有和一个传入请求有关的底层信息,如提交的标单中的数据等等。所以 说,asp.net中的托管代码想要访问aspnet_isapi.dll对外提供的接口,就需要通过ECB。其实更准确的来说,是托管代码公布了一个 IUnknown类型的接口供aspnet_isapi.dll调用,而aspnet_isapi.dll在调用的时候会把自己的ecb地址传进去。
   明白了ECB的概念,下面我们要介绍一个接口和一个接口的实现类(位于System.Web.Hosting名字空间下),请读者注意笔者在代码中的注释 (本文的主要目的就是和大家一起从代码实现的角度来认识整个Asp.net运行时,所以代码里的注释是笔者添加的关键性说明,后面的所有代码段都是这 样):
  
  IISAPIRuntime
   1/**//*InterfaceType(ComInterfaceType.InterfaceIsIUnknown)指明了这个接口将作为 IUnknown 派生接口向 COM 公开,这就使得isapi.dll可以以COM方式调用此接口。*/
   2[ComImport, Guid("08a2c56f-7c16-41c1-a8be-432917a1a2d1"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
   3public interface IISAPIRuntime
   4{
   5 void StartProcessing();
   6 void StopProcessing();
   7 /**//*ProcessRequest方法就是整个处理流程中托管代码和非托管代码的分界点,可以看到里面是以一个IntPtr结构传入了调用方(也就是isapi.dll)的ECB地址*/
   8 [return: MarshalAs(UnmanagedType.I4)]
   9 int ProcessRequest([In] IntPtr ecb, [In, MarshalAs(UnmanagedType.I4)] int useProcessModel);
  10 void DoGCCollect();
  11}
  12
  13/**//*这个类实现了IISAPIRuntime接口。它的实例对象存在于每一个AppDomain中,作为整个Asp.net运行时的入口。*/
  14public sealed class ISAPIRuntime : MarshalByRefObject, IISAPIRuntime, IRegisteredObject
  15{
  16 // Fields
  17 private static int _isThisAppDomainRemovedFromUnmanagedTable;
  18 private static string s_thisAppDomainsIsapiAppId;
  19
  20 // Methods
  21 [AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.Minimal), SecurityPermission(SecurityAction.Demand, Unrestricted=true)]
  22 public ISAPIRuntime();
  23 public void DoGCCollect();
  24 public override object InitializeLifetimeService();
  25 /**//*处理请求的入口点方法,由isapi.dll以COM方式调用*/
  26 public int ProcessRequest(IntPtr ecb, int iWRType);
  27 internal static void RemoveThisAppDomainFromUnmanagedTable();
  28 internal void SetThisAppDomainsIsapiAppId(string appId);
  29 public void StartProcessing();
  30 public void StopProcessing();
  31 void IRegisteredObject.Stop(bool immediate);
  32}
  所以,一切都是从aspnet_isapi.dll以COM方式调用了一个ISAPIRuntime对象的ProcessRequest方法 开始的。可以多提一句的是,这种调用是异步的,也就是说,aspnet_isapi.dll在调用后会立即返回,但ECB会一直保留下来,直到整个请求被 处理完毕之后再释放。
   好,现在我们知道了ISAPIRuntime对象是托管代码的入口点,那么这个对象是什么时候产生的呢?换句话说,w3wp也是一个非托管代码写出的程 序,它是在什么时候把.net运行时加载进来的呢?(如果好奇心再强一点,还可以问一问一个工作进程是什么时刻产生并开始运行的,它和应用程序池有着怎样 的交互。)完全解释清楚这些问题已经超过了笔者目前的能力范围,还望高人补充或提供资料线索。但目前我们从.net的代码中应该可以推断 出,ISAPIRuntime对象和应用程序域是对应的,.net在创建应用程序域的时候,就会创建ISAPIRuntime对象,见下面的创建应用程序 域的代码:
  创建应用程序域
   1/**//*这是System.Web.Hosting.AppDomainFactory类型的Create方法,它调用的是实际工厂的Create方法。*/
   2[return: MarshalAs(UnmanagedType.Interface)]
   3public object Create(string module, string typeName, string appId, string appPath, string strUrlOfAppOrigin, int iZone)
   4{
   5 /**//*实际工厂是一个AppManagerAppDomainFactory类型的对象。*/
   6 return this._realFactory.Create(appId, appPath);
   7}
   8
   9/**//*AppManagerAppDomainFactory.Create方法,请看代码内的注释。*/
  10[return: MarshalAs(UnmanagedType.Interface)]
  11public object Create(string appId, string appPath)
  12{
  13 object obj2;
  14 try
  15 {
  16 if (appPath[0] == '.')
  17 {
  18 FileInfo info = new FileInfo(appPath);
  19 appPath = info.FullName;
  20 }
  21 if (!StringUtil.StringEndsWith(appPath, '//'))
  22 {
  23 appPath = appPath + @"/";
  24 }
  25 ISAPIApplicationHost appHost = new ISAPIApplicationHost(appId, appPath, false);
  26 /**//*这个方法内部的调用链非常复杂,它一方面创建了一个应用程序域,一方面返回一个ISAPIRuntime对象。具体这个方法究竟是如何创建AppDomain对象的,大家可以用
  27 JetBrain来跟踪其调用栈。关于这部分内容更详尽的信息,可参见ASP.NET Internals - The bridge between ISAPI and Application Domains一文。
  28 另外,如果您使用JetBrain来调试系统程序集的话,有可能会因为缺少相应pdb文件而不能查看完整调试信息,这里提供一个根据已有程序集,先反汇编成中间码,
  29 再重新以调试模式生成dll和pdb文件的方法:
  30 1)生成IL文件: ildasm /tok /byt system.web.dll /out=system.web.il
  31 2)重新生成PDB/DLL: ilasm system.web.il /DEBUG /DLL /OUTPUT=System.Web.dll*/
  32 ISAPIRuntime o = (ISAPIRuntime) this._appManager.CreateObjectInternal(appId, typeof(ISAPIRuntime), appHost, false, null);
  33 o.SetThisAppDomainsIsapiAppId(appId);
  34 o.StartProcessing();
  35 obj2 = new ObjectHandle(o);
  36 }
  37 catch (Exception)
  38 {
  39 throw;
  40 }
  41 return obj2;
  42}
  
  三.Asp.net运行时,我们等待已久的纯托管代码环境
   好,经过上面长久的铺垫,我们终于进入了托管代码的领域。经过前面的内容,我们知道,在托管代码中首先被执行的是一个ISAPIRuntime对象的ProcessRequest方法,那么下面我们就来看一看这个方法主要做了些什么:
  
  ISAPIRuntime.ProcessRequest
   1/**//*ISAPIRuntime的方法,处理请求的入口。*/
   2public int ProcessRequest(IntPtr ecb, int iWRType)
   3{
   4 try
   5 {
   6 /**//*这里ecb被作为参数传入,返回一个HttpWorkerRequest类型的对象,作为对一个请求的数据的封装。但HttpWorkerRequest
   7 *只是一个抽象基类,CreateWorkerRequest作为一个工厂方法,返回的实际类型是ISAPIWorkerRequestInProc,
   8 *ISAPIWorkerRequestInProcForIIS6或ISAPIWorkerRequestOutOfProc。这些类型里面提供的方法,其实大多
   9 *围绕着如何从ecb中去获取数据,所以都包含了很多对System.Web.UnsafeNativeMethods类型中静态方法的调用。
  10 **/
  11 HttpWorkerRequest wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, iWRType);
  12 string appPathTranslated = wr.GetAppPathTranslated();
  13 string appDomainAppPathInternal = HttpRuntime.AppDomainAppPathInternal;
  14 if ((appDomainAppPathInternal == null) || StringUtil.EqualsIgnoreCase(appPathTranslated, appDomainAppPathInternal))
  15 {
  16 /**//*从这里开始,对请求的处理流程就交给了HttpRuntime。需要注意的是,ISAPI是多线程的,而且对ProcessRequest的调用是异步的,
  17 *这就要求HttpRuntime.ProcessRequest方法是线程安全的。看一看HttpRuntime.ProcessRequestNoDemand里的代码大家就清楚,
  18 *所有的请求会被排成一个队列,顺次执行,保证了并发安全。
  19 *最终,HttpRuntime.ProcessRequestInternal方法会被调用,我们接下来就去看看那个方法。
  20 **/
  21 HttpRuntime.ProcessRequestNoDemand(wr);
  22 return 0;
  23 }
  24 HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged, SR.GetString("Hosting_Phys_Path_Changed", new object[] { appDomainAppPathInternal, appPathTranslated }));
  25 }
  26 catch (Exception exception)
  27 {
  28 Misc.ReportUnhandledException(exception, new string[] { SR.GetString("Failed_to_process_request") });
  29 throw;
  30 }
  31 return 1;
  32}
  上面的代码段最主要的作用就是调用了HttpRumtime.ProcessRequestInternal方法,下面我们就一起来看看这个方法的实现:
  HttpRuntime.ProcessRequestInternal
   1/**//*在HttpRuntime.ProcessRequestInternal()方法里,有如下几个重要的对象被创建出来:
   2 *(1)HttpContext(包括其中的HttpRequest,HttpResponse)
   3 *(2)HttpApplication
   4 *同时,会执行HttpApplication对象的ProcessRequest方法,
   5 */
   6private void ProcessRequestInternal(HttpWorkerRequest wr)
   7{
   8 /**//*HttpContext对象在这里被创建。HttpWorkerRequest做为构造参数,而HttpWorkerRequest本身
   9 *又围绕着对ecb的处理建立了一群高层的方法,它的实例会被HttpContext传给HttpRequest和HttpResponese
  10 *做为他们的构造参数。所以,这里也能更清楚地看出HttpWorkerRequest作为ecb的托管环境封装器的实质。
  11 *另外,这里也能清楚地反映出,每一个请求都有一个自己的HttpContext对象(而每一个HttpContext对象都管理着
  12 *一个HttpSession对象--参见HttpContext的Session属性,这也就保证了每个访问者有自己的session对象。),你可以
  13 *使用HttpContext.Current来访问到这个对象。
  14 */
  15 HttpContext extraData = new HttpContext(wr, false);
  16 wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, extraData);
  17 Interlocked.Increment(ref this._activeRequestCount);
  18 HostingEnvironment.IncrementBusyCount();
  19 try
  20 {
  21 try
  22 {
  23 this.EnsureFirstRequestInit(extraData);
  24 }
  25 catch
  26 {
  27 if (!extraData.Request.IsDebuggingRequest)
  28 {
  29 throw;
  30 }
  31 }
  32 extraData.Response.InitResponseWriter();
  33 /**//*用应用程序工厂返回一个HttpApplication对象。
  34 *和线程池对线程的管理相似,HttpApplicationFactory中以stack维护了一个HttpApplication的列表(参见HttpApplicationFactory
  35 *的_freeList变量)。在这句方法调用的最后,实际是调用了 _theApplicationFactory.GetNormalApplicationInstance(context),
  36 *里面就是从_freeList的栈顶pop出一个已经构造的HttpApplication实例。
  37 *所以,对于每一个请求,由HttpContext作为上下文,由一个HttpApplication对象来控制整个应用处理的pipeline,整个
  38 *处理过程是在由工作进程管理的线程池中的某个线程内完成的。
  39 *另外,在一个应用程序域内,由于可以同时处理多个请求,所以就有多个HttpApplication实例和多个活动线程(您可以使用windbg的sos
  40 *扩展来观察它们之间的关系,本文就不继续深入了)。
  41 *还有,对所有HttpModules的加载就是发生在HttpApplication对象的创建过程之中(包括系统已经提供的Authentication等模块儿和我们
  42 *的自定义模块)。我们可以在Web.config里声明自己的自定义模块。这些模块的作用就是在整个HttpApplication处理管线的相关事件点上,
  43 *挂上自己的处理。注意一下IHttpModule接口的Init()方法的声明,这个方法的传入参数就是要被创建的HttpApplication对象,所以,如果
  44 *你自己的模块想在缓存读取上加入一些自定义操作,你只需进行如下处理即可:
  45 public class YourCustomModule : IHttpModule
  46 {
  47 public void Init(HttpApplication application)
  48 {
  49 application.ResolveRequestCache += new EventHandler(this.YourCustomResolveRequestCache);
  50 }
  51 }
  52 *另外,通过对HttpApplicationFactory.GetApplicationInstance方法内部实现方式的阅读,你会发现在每一个HttpApplication对象被创建
  53 *之后,会立刻调用这个对象的InitInternal方法,而这个方法里面做了很多重要的初始化操作,内容较多,我们将在下文中单独介绍。
  54 *
  55 */
  56 IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(extraData);
  57 if (applicationInstance == null)
  58 {
  59 throw new HttpException(SR.GetString("Unable_create_app_object"));
  60 }
  61 if (EtwTrace.IsTraceEnabled(5, 1))
  62 {
  63 EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, extraData.WorkerRequest, applicationInstance.GetType().FullName, "Start");
  64 }
  65 /**//*看一下System.Web.HttpApplication的类型声明
  66 *public class HttpApplication : IHttpAsyncHandler, IHttpHandler, IComponent, IDisposable
  67 *你会发现它同时实现了同步和异步的IHandler,所以在默认情况下,Asp.net对请求的处理是异步的。
  68 */
  69 if (applicationInstance is IHttpAsyncHandler)
  70 {
  71 IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance;
  72 extraData.AsyncAppHandler = handler2;
  73 /**//*BeginProcessRequest会调用HttpApplication的ResumeSteps()方法,在ResumeSteps()中完成了整个应用程序周期的所有操作,
  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;第二它屏蔽了引发事件的应用执行步骤和普通的内置执 行步骤之间的差别;第三是比较容易在以后对整个流程进行改进和扩展。
  
  本文结束,但对Asp.net运行时和.Net框架的探究其实只是刚刚开了个头。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值