一、概念入手,什么是ASP.NET2.0运行时?
ASP.NET运行时指从asp.net框架接收到客户端请求,到合适的HttpHandle接管求求之间,以及从HttpHandlers生成处理结果后到发送请求处理结果到客户端之间,ASP.NET框架所完成的一系列工作和逻辑.
从概念上来看似乎看不出什么端倪,也看不出关于asp.net2.0运行时的任何内部机理.所以我们从ASP.NET应用程序的宿主IIS说起,一般我们发布一个asp.net应用程序时,我们都会在IIS中选择一个应用池,一般默认的是DefaultAppPool,当然我们可以建立多个应用池,但建立这么多的应用池有什么用呢?一般来说一个应用池可以也只能设置一个版本的asp.net Framework,还可以设置它的最大内存数量、CPU使用率、回收工作进程的时间间隔等参数,因此设置多个应用池可以对应地发布不同的asp.net应用程序,默认情况下,每个应用程序池由一个w3wp.exe进程来维护(这也是为什么每次我们本地IIS部署并开启一个asp.net网站时会有一个w3wp.exe进程在那运行着).
IIS中不仅仅可以发布asp.net应用程序还可以发布asp,php应用程序,如何区别这些不同应用程序发过来的请求呢?其实,IIS处理这些不同的请求是通过各种ISAPI的扩展来执行的,IIS根据不同的请求,将请求指定到不同的ISAPI扩展,如果说客户端发送请求的是一个aspx页面,IIS就会将这个请求发给asp.net特有的扩展aspnet_isapi处理,如果我们未安装这个扩展,就会提示"HTTP 错误 404,您正在搜索的页面可能已经删除、更名或暂时不可用 ".至于aspnet_isapi如何处理这个请求,我们可以先从ISAPIRunTime这个类入手.
二、ISAPIRunTime
这个类位于System.Web.Hosting命名空间下,是一个密封类,aspnet_isapi对于IIS交给它的请求它第一时间以COM方式调用了IISAPIRunTime对象的ProcessRequest方法,这也是从托管环境至非托管环境的一个交界点.aspnet_isapi.dll存在于非托管环境中,它通过加载CLR创建了一个托管的环境,而这个过程是异步的,aspnet_isapi一调用ISAPIRunTime对象的ProcessRequest方法后就立即返回,只将自己对应的ECB的指针传给它,ISAPIRutime不但可以将最终生成的Response返回给ISAPI,还能通过ECB调用ISAPI获得一些所需的数据。
{
try
{
//ecb是aspnet_isapi异步调用ISAPIRunTime对象的ProcessRequest方法后传递的指针,这也是从托管环境进入非托管环境的一个交界点.
HttpWorkerRequest wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, iWRType);
string appPathTranslated = wr.GetAppPathTranslated();
string appDomainAppPathInternal = HttpRuntime.AppDomainAppPathInternal;
if ((appDomainAppPathInternal == null) || StringUtil.EqualsIgnoreCase
(appPathTranslated, appDomainAppPathInternal))
{
HttpRuntime.ProcessRequestNoDemand(wr);
return 0;
}
HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged,
SR.GetString("Hosting_Phys_Path_Changed", new object[] { appDomainAppPathInternal, appPathTranslated }));
}
catch (Exception exception)
{
Misc.ReportUnhandledException(exception, new string[]
{ SR.GetString("Failed_to_process_request") });
throw;
}
return 1;
}
三、HttpRunTime
HttpRunTime这个类位于System.Web命名空间下,是System.Web组件中最为复杂的几个类之一.这是一个密封类,因此我们不能去继承修改它.这个类在asp.net2.0运行时起着一个主导的作用.从HttpRunTime这个对象的ProcessRequestInternal方法我们来看看它到底做了什么.
{
//创建一个HttpContext实例,每一个请求都会对应一个HttpContext对象,而每一个对象都管理着一个HttpSession对象,这也保证了每次请求均有一个Session对象可以使用.
HttpContext extraData = new HttpContext(wr, false);
wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, extraData);
Interlocked.Increment(ref this._activeRequestCount);
HostingEnvironment.IncrementBusyCount();
try
{
try
{
//对第一次请求初始化,在这个过程中通过调用System.Web.HttpRuntime.FirstRequestInit进行一些初始化工作,比如:将Web.Config配置读到到RuntimeConfig中,从bin目录中装载所有dll文件
this.EnsureFirstRequestInit(extraData);
}
catch
{
if (!extraData.Request.IsDebuggingRequest)
{
throw;
}
}
extraData.Response.InitResponseWriter();
IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(extraData);
//HttpApplication是在这里创建的,这里引用dudu对这个方法的分析.
通过调用HttpApplicationFactory.GetApplicationInstance创建HttpApplication实例。
在HttpApplicationFactory.GetApplicationInstance中有三个关键方法:
HttpApplicationFactory._theApplicationFactory.EnsureInited();
HttpApplicationFactory._theApplicationFactory.EnsureAppStartCalled(context);
HttpApplicationFactory._theApplicationFactory.GetNormalApplicationInstance(context);
下面我们对这三个方法逐个进行分析:
1) HttpApplicationFactory._theApplicationFactory.EnsureInited();
该方法检查HttpApplicationFactory是否被初始化,如果没有,就通过HttpApplicationFactory.Init()进行初始化。在Init()中,先获取global.asax文件的完整路径,然后调用CompileApplication()对global.asax进行编译。
那编译是如何进行的呢?
编译的工作由BuildManager完成的。BuildManager先得到GlobalAsaxType(也就是HttpApplication),
然后调用BuildManager.GetGlobalAsaxBuildResult()=》
GetGlobalAsaxBuildResultInternal()=》EnsureTopLevelFilesCompiled()进行编译。
在EnsureTopLevelFilesCompiled中,先进行CompilationStage.TopLevelFiles编译,对下面三个目录中的文件进行编译:
a. CompileResourcesDirectory();
编译App_GlobalResources目录。
b. CompileWebRefDirectory();
编译App_WebReferences目录。
c. CompileCodeDirectories();
编译App_Code目录。
接着进行CompilationStage.GlobalAsax
编译,对global.asax进行编译,方法调用情况:CompileGlobalAsax()=》
ApplicationBuildProvider.GetGlobalAsaxBuildResult(BuildManager.IsPrecompiledApp)。
在GetGlobalAsaxBuildResult中具体的编译是由ApplicationBuildProvider与BuildProvidersCompiler共同完成的。
BuildProvidersCompiler.PerformBuild();进行编译工作。
ApplicationBuildProvider.GetBuildResult得到编译的结果。
编译成功后,会在C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\相应的目录中生成类似App_global.asax.mlgx7n2v.dll的dll文件。
编译生成的类名为ASP.global_asax,继承自HttpApplication。
注:如果Web目录中没有Global.asax文件,就不会编译生成App_global.asax.mlgx7n2v.dll这样的文件。
2) HttpApplicationFactory._theApplicationFactory.EnsureAppStartCalled(context);
创建特定的HttpApplication实例,触发ApplicationOnStart事件,执行ASP.global_asax中的
Application_Start(object sender, EventArgs
e)方法。这里创建的HttpApplication实例在处理完事件后,就被回收。
该 方法创建HttpApplication实例并进行初始化(调用System.Web.HttpApplication. InitInternal()方法)。
创 建HttpApplication实例是根据实际的_theApplicationType进行创建。如果Web目录中没有global.asax文件,也 就是说没有动态编译生成ASP.global_asax类型,那就直接实例化HttpApplication。如果创建了ASP.global_asax 类型,那就对ASP.global_asa进行实例化。
if (applicationInstance == null)
{
throw new HttpException(SR.GetString("Unable_create_app_object"));
}
if (EtwTrace.IsTraceEnabled(5, 1))
{
EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, extraData.WorkerRequest,
applicationInstance.GetType().FullName, "Start");
}
if (applicationInstance is IHttpAsyncHandler)
{
IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance;
extraData.AsyncAppHandler = handler2;
handler2.BeginProcessRequest(extraData, this._handlerCompletionCallback, extraData);
}
else
{
applicationInstance.ProcessRequest(extraData);
this.FinishRequest(extraData.WorkerRequest, extraData, null);
}
}
catch (Exception exception)
{
extraData.Response.InitResponseWriter();
this.FinishRequest(wr, extraData, exception);
}
}
asp.net2.0运行时牵涉到的内容很多,本人对.net运行时的理解或许只是停留在表面上的,如果朋友对.net运行时感兴趣的话,可以学习如下几位大牛的文章,本文的很大一部分也是参考如下文章,希望本篇文章能对你学习asp.net2.0运行时带来一点帮助.
dudu的asp.net运行时简要分析
EagleFish的从.net类库看.net运行时
Artech的ASP.NET Process Model之二:ASP.NET Http RunTime Pipeline-PartI
Artech的ASP.NET Process Model之二:ASP.NET Http RunTime Pipeline-PartII
文野的一点一点学ASP.NET之基础概念——HTTP运行期与页面执行模型
头中中,不大的ASP.NET的(HttpModule,HttpHandler)