在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
声明:本博客上所有文章均为EagleFish在cnblogs上的原创,欢迎转载,但请注明出处。
}
所以,一切都是从aspnet_isapi.dll以COM方式调用了一个ISAPIRuntime对象的ProcessRequest方法开始的。可以多提一句的是,这种调用是异步的,也就是说,aspnet_isapi.dll在调用后会立即返回,但ECB会一直保留下来,直到整个请求被处理完毕之后再释放。 好,现在我们知道了ISAPIRuntime对象是托管代码的入口点,那么这个对象是什么时候产生的呢?换句话说,w3wp也是一个非托管代码写出的程序,它是在什么时候把.net运行时加载进来的呢?(如果好奇心再强一点,还可以问一问一个工作进程是什么时刻产生并开始运行的,它和应用程序池有着怎样的交互。)完全解释清楚这些问题已经超过了笔者目前的能力范围,还望高人补充或提供资料线索。但目前我们从.net的代码中应该可以推断出,ISAPIRuntime对象和应用程序域是对应的,.net在创建应用程序域的时候,就会创建ISAPIRuntime对象,见下面的创建应用程序域的代码:
}
三.Asp.net运行时,我们等待已久的纯托管代码环境
}
上面的代码段最主要的作用就是调用了HttpRumtime.ProcessRequestInternal方法,下面我们就一起来看看这个方法的实现:
}
从上面的代码可以看出,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 把整个数组拷贝给HttpApplication的私有变量_execSteps
可以看到,对执行步骤数组的初始化有两种形式,一种是直接添加一个步骤:steps.Add(new MapHandlerExecutionStep(this)); 这种执行步骤是和事件无关的,只是在整个事件流中的特定位置执行一些特定的操作(实例化Handler之类的)。 而另一种是要把相关事件的处理方法列表添加到步骤中,每一个步骤其实是对一个事件的处理: this.CreateEventExecutionSteps(EventBeginRequest, steps); (8)执行步骤数组的执行。 其实在前文中已经提到过了,整个执行步骤数组的执行,是在HttpApplication.ResumeSteps()中调用的。恐怕就算不看这个方法的代码,大家也能想象得出,它是遍历整个执行步骤数组,然后调用其中每一项的Execute方法。这里大家大概也就清楚了,为什么会有一个执行步骤的概念。以笔者看来,首先它在概念上很好理解,完全贴合整个应用处理的管道模型pipeline;第二它屏蔽了引发事件的应用执行步骤和普通的内置执行步骤之间的差别;第三是比较容易在以后对整个流程进行改进和扩展。 本文结束,但对Asp.net运行时和.Net框架的探究其实只是刚刚开了个头。
|
请发表评论