所属类别:.NET
文章作者:未知
特别推荐:免费发布信息 承包关键词~~抢爆了!HOT!
http://flier_lu.blogone.net?id=1544105.NET1.1中预编译ASP.NET页面实现原理浅析MS在发布ASP.NET时的一大功能特性是,与ASP和PHP等脚本语言不同,ASP.NET实际上是一种编译型的快速网页开发环境。这使得ASP.NET在具有开发和修改的简便性的同时,不会负担效率方面的损失。实现上ASP.NET与JSP的思路类似,引擎在第一次使用一个页面之前,会将之编译成一个类,自动生成Assembly并载入执行。而通过《在WinForm程序中嵌入ASP.NET》一文中我们可以了解到,ASP.NET引擎实际上是可以无需通过IIS等Web服务器调用而被使用的,这就使得手工预编译ASP.NET页面成为可能。实际上这个需求是普遍存在的,早在ASP时代就层有第三方产品支持将ASP页面编译成二进制程序,以提高执行效率和保障代码安全性,而将伴随Whidbey发布的ASP.NET2.0更是直接内置了预编译ASP.NET页面的功能。实际上网上早就有人讨论过在ASP.NET1.1中模拟预编译特性的实现方法,例如以下两篇文章Pre-CompilingASP.NETWebPagesPre-CompileASPXpagesin.NET1.1其思路基本上都是遍历所有需要预编译的页面文件,然后通过模拟Web页面请求的方式,触发ASP.NET引擎的自动预编译机制。这样做的好处是完全模拟真实情况,无需了解ASP.NET引擎的实现原理;但同时也会受到诸多限制,如预编译结果不透明,无法脱离原始ASP.NET页面文件使用等等,而且无法使我们从原理上理解预编译特性的实现。下面我将分三到四个小节,简要讨论ASP.NET自动编译机制的实现、ASP.NET页面文件编译的实现以及如何在ASP.NET1.1中实现手动预编译页面和相应分发机制。[1]自动预编译机制浅析本节我们将详细分析讨论.NET1.1中,ASP.NET引擎内部实现自动页面预编译的原理。首先,我们所说的ASP.NET页面实际上主要分为四类:1.Web应用程序文件Global.asax2.Web页面文件*.aspx3.用户自定义控件文件*.ascx4.Web服务程序文件*.asmxWeb应用程序文件对于每个Web应用程序来说是可选唯一的,用来处理ASP.NET应用程序一级的事件,并将被预编译为一个System.Web.HttpApplication类的子类;Web页面文件是普通的ASP.NET页面,处理特定页面的事件,将被预编译为一个System.Web.UI.Page类的子类;用户自定义控件文件是特殊的ASP.NET页面,处理控件自身的事件,将被预编译为一个System.Web.UI.UserControl类的子类;Web服务程序文件则是与前三者不太相同的一种特殊页面文件,暂时不予讨论。然后,前三种ASP.NET文件的编译时机也不完全相同。Web应用程序文件在此Web应用程序文件第一次被使用时自动编译;Web页面文件在此Web页面第一次被使用时自动编译,实际上是调用HttpRuntime.ProcessRequest函数触发预编译;用户自定义控件文件则在其第一次被Web页面使用的时候自动编译,实际上是调用Page.LoadControl函数触发预编译。在了解了以上这些基本知识后,我们来详细分析一下自动预编译的实现机制。HttpRuntime.ProcessRequest函数是处理Web页面请求的调用发起者,伪代码如下:以下为引用: publicstaticvoidHttpRuntime.ProcessRequest(HttpWorkerRequestwr){//检查当前调用者有没有作为ASP.NET宿主(Host)的权限InternalSecurityPermissions.AspNetHostingPermissionLevelMedium.Demand();if(wr==null){thrownewArgumentNullException("custom");}RequestQueuequeue=HttpRuntime._theRuntime._requestQueue;if(queue!=null){//将参数中的Web页面请求放入请求队列中//并从队列中使用FIFO策略获取一个页面请求wr=queue.GetRequestToExecute(wr);}if(wr!=null){//更新性能计数器HttpRuntime.CalculateWaitTimeAndUpdatePerfCounter(wr);//实际完成页面请求工作HttpRuntime.ProcessRequestNow(wr);}}HttpRuntime.ProcessRequestNow函数则直接调用缺省HttpRuntime实例的ProcessRequestInternal函数完成实际页面请求工作,伪代码如下:以下为引用: internalstaticvoidHttpRuntime.ProcessRequestNow(HttpWorkerRequestwr){HttpRuntime._theRuntime.ProcessRequestInternal(wr);}HttpRuntime.ProcessRequestInternal函数逻辑稍微复杂一些,大致可分为四个部分。首先检查当前HttpRuntime实例是否第一次被调用,如果是第一次调用则通过FirstRequestInit函数初始化;接着调用HttpResponse.InitResponseWriter函数初始化页面请求的返回对象HttpWorkerRequest.Response;然后调用HttpApplicationFactory.GetApplicationInstance函数获取当前Web应用程序实例;最后使用Web应用程序实例完成实际的页面请求工作。伪代码如下:以下为引用: privatevoidHttpRuntime.ProcessRequestInternal(HttpWorkerRequestwr){//构造HTTP调用上下文对象HttpContextctxt=newHttpContext(wr,0);//设置发送结束异步回调函数wr.SetEndOfSendNotification(this._asyncEndOfSendCallback,ctxt);//更新请求计数器Interlocked.Increment(&(this._activeRequestCount));try{//检查当前HttpRuntime实例是否第一次被调用if(this._beforeFirstRequest){lock(this){//使用Double-Checked模式避免冗余锁定if(this._beforeFirstRequest){this._firstRequestStartTime=DateTime.UtcNow;this.FirstRequestInit(ctxt);//初始化当前HttpRuntime运行时环境this._beforeFirstRequest=false;}}}//根据配置文件设置,扮演具有较高特权的角色ctxt.Impersonation.Start(true,false);try{//初始化页面请求的返回对象ctxt.Response.InitResponseWriter();}finally{ctxt.Impersonation.Stop();}//获取当前Web应用程序实例IHttpHandlerhandler=HttpApplicationFactory.GetApplicationInstance(ctxt);if(handler==null){thrownewHttpException(HttpRuntime.FormatResourceString("Unable_create_app_object"));}//使用Web应用程序实例完成实际的页面请求工作if((handlerasIHttpAsyncHandler)!=null){IHttpAsyncHandlerasyncHandler=((IHttpAsyncHandler)handler);ctxt.AsyncAppHandler=asyncHandler;//使用异步处理机制asyncHandler.BeginProcessRequest(ctxt,this._handlerCompletionCallback,ctxt);}else{handler.ProcessRequest(ctxt);this.FinishRequest(ctxt.WorkerRequest,ctxt,null);}}catch(ExceptionE){ctxt.Response.InitResponseWriter();this.FinishRequest(wr,ctxt,E);}}HttpRuntime.ProcessRequestInternal函数中,涉及到文件预编译的有两部分:一是获取当前Web应用程序实例时,会根据情况自动判断是否预编译Web应用程序文件;二是在完成实际页面请求时,会在第一次使用某个页面时触发预编译行为。首先来看看对Web应用程序文件的处理。HttpRuntime.ProcessRequestInternal函数中调用了HttpApplicationFactory.GetApplicationInstance函数获取当前Web应用程序实例。System.Web.HttpApplicationFactory是一个内部类,用以实现对多个Web应用程序实例的管理和缓存。GetApplicationInstance函数返回的是一个IHttpHandler接口,提供IHttpHandler.ProcessRequest函数用于其后对Web页面文件的处理。伪代码如下:以下为引用: internalstaticIHttpHandlerHttpApplicationFactory.GetApplicationInstance(HttpContextctxt){//定制应用程序if(HttpApplicationFactory._customApplication!=null){returnHttpApplicationFactory._customApplication;}//调试请求if(HttpDebugHandler.IsDebuggingRequest(ctxt)){returnnewHttpDebugHandler();}//判断是否需要初始化当前HttpApplicationFactory实例if(!HttpApplicationFactory._theApplicationFactory._inited){HttpApplicationFactoryfactory=HttpApplicationFactory._theApplicationFactory;lock(HttpApplicationFactory._theApplicationFactory);{//使用Double-Checked模式避免冗余锁定if(!HttpApplicationFactory._theApplicationFactory._inited){//初始化当前HttpApplicationFactory实例HttpApplicationFactory._theApplicationFactory.Init(ctxt);HttpApplicationFactory._theApplicationFactory._inited=true;}}}//获取Web应用程序实例returnHttpApplicationFactory._theApplicationFactory.GetNormalApplicationInstance(ctxt);}在处理特殊情况和可能的实例初始化之后,调用HttpApplicationFactory.GetNormalApplicationInstance函数完成获取Web应用程序实例的实际功能,伪代码如下:以下为引用: privateHttpApplicationHttpApplicationFactory.GetNormalApplicationInstance(HttpContextcontext){HttpApplicationapp=null;//尝试从已施放的Web应用程序实例队列中获取lock(this._freeList){if(this._numFreeAppInstances>0){app=(HttpApplication)this._freeList.Pop();this._numFreeAppInstances--;}}if(app==null){//构造新的Web应用程序实例app=(HttpApplication)System.Web.HttpRuntime.CreateNonPublicInstance(this._theApplicationType);//初始化Web应用程序实例app.InitInternal(context,this._state,this._eventHandlerMethods);}returnapp;}构造新的Web应用程序实例的代码很简单,实际上就是对Activator.CreateInstance函数的简单包装,伪代码如下:以下为引用: internalstaticobjectHttpRuntime.CreateNonPublicInstance(Typetype,object[]args){returnActivator.CreateInstance(type,BindingFlags.CreateInstanceBindingFlags.InstanceBindingFlags.NonPublicBindingFlags.Public,null,args,null);}internalstaticobjectHttpRuntime.CreateNonPublicInstance(Typetype){returnHttpRuntime.CreateNonPublicInstance(type,null);}至此一个Web应用程序实例就被完整构造出来,再经过InitInternal函数的初始化,就可以开始实际页面处理工作了。而HttpApplicationFactory实例的_theApplicationType类型,则是结果预编译后的Global.asax类。实际的预编译工作在HttpApplicationFactory.Init函数中完成,伪代码如下:以下为引用: privatevoidHttpApplicationFactory.Init(HttpContextctxt){if(HttpApplicationFactory._customApplication!=null)return;using(HttpContextWrapperwrapper=newHttpContextWrapper(ctxt)){ctxt.Impersonation.Start(true,true);try{try{this._appFilename=HttpApplicationFactory.GetApplicationFile(ctxt);this.CompileApplication(ctxt);this.SetupChangesMonitor();}finally{ctxt.Impersonation.Stop();}}catch(Object){}this.FireApplicationOnStart(ctxt);}}GetApplicationFile函数返回Web请求物理目录下的global.asax文件路径;CompileApplication函数则根据此文件是否存在,判断是预编译之并载入编译后类型,还是直接返回缺省的HttpApplication类型,伪代码如下:以下为引用: internalstaticstringHttpApplicationFactory.GetApplicationFile(HttpContextctxt){returnPath.Combine(ctxt.Request.PhysicalApplicationPath,"global.asax");}privatevoidHttpApplicationFactory.CompileApplication(HttpContextctxt){if(FileUtil.FileExists(this._appFilename)){ApplicationFileParserparser;//获取编译后的Web应用程序类型this._theApplicationType=ApplicationFileParser.GetCompiledApplicationType(this._appFilename,context,outparser);this._state=newHttpApplicationState(parser1.ApplicationObjects,parser.SessionObjects);this._fileDependencies=parser.SourceDependencies;}else{this._theApplicationType=typeof(HttpApplication);this._state=newHttpApplicationState();}this.ReflectOnApplicationType();}分析到这里我们可以发现,内部类型System.Web.UI.ApplicationFileParser的GetCompiledApplicationType函数是实际上进行Web应用程序编译工作的地方。但现在我们暂且打住,等下一节分析编译过程时再详细解说。:)然后我们看看对Web页面文件的处理。在前面分析HttpRuntime.ProcessRequestInternal函数时我们曾了解到,在获得了Web应用程序实例后,会使用此实例的IHttpAsyncHandler接口或IHttpHandler接口,完成实际的页面请求工作。而无论有否Global.asax文件,最终返回的Web应用程序实例都是一个HttpApplication类或其子类的实例,其实现了IHttpAsyncHandler接口,支持异步的Web页面请求工作。对此接口的处理伪代码如下:以下为引用: privatevoidHttpRuntime.ProcessRequestInternal(HttpWorkerRequestwr){...//使用Web应用程序实例完成实际的页面请求工作if((handlerasIHttpAsyncHandler)!=null){IHttpAsyncHandlerasyncHandler=((IHttpAsyncHandler)handler);ctxt.AsyncAppHandler=asyncHandler;//使用异步处理机制asyncHandler.BeginProcessRequest(ctxt,this._handlerCompletionCallback,ctxt);}else{handler.ProcessRequest(ctxt);this.FinishRequest(ctxt.WorkerRequest,ctxt,null);}...}HttpRuntime.ProcessRequestInternal函数通过调用HttpApplication.IHttpAsyncHandler.BeginProcessRequest函数开始页面请求工作。而HttpApplication实际上根本不支持同步形式的IHttpHandler接口,伪代码如下:以下为引用: voidHttpApplication.ProcessRequest(System.Web.HttpContextcontext){thrownewHttpException(HttpRuntime.FormatResourceString("Sync_not_supported"));}boolHttpApplication.get_IsReusable(){returntrue;}而在HttpApplication.IHttpAsyncHandler.BeginProcessRequest函数中,将完成非常复杂的异步调用后台处理操作,这儿就不多罗嗦了,等有机会写篇文章专门讨论一下ASP.NET中的异步操作再说。而其最终调用还是使用System.Web.UI.PageParser对需要处理的Web页面进行解析和编译。最后我们看看对用户自定义控件文件的处理。Page类的LoadControl函数实际上是在抽象类TemplateControl中实现的,伪代码如下:以下为引用: publicControlLoadControl(stringvirtualPath){virtualPath=UrlPath.Combine(base.TemplateSourceDirectory,virtualPath);Typetype=UserControlParser.GetCompiledUserControlType(virtualPath,null,base.Context);returnthis.LoadControl(type1);}实际的用户自定义控件预编译操作还是在UserControlParser类中完成的。至此,在这一节中我们已经大致了解了ASP.NET自动预编译的实现原理,以及在什么时候对页面文件进行预编译。下一节我们将详细分析ApplicationFileParser、PageParser和UserControlParser,了解ASP.NET是如何对页面文件进行预编译的。关闭本页
相关信息· 简单新闻新闻公告!!(php,不用数据库)
· 8月操作系统市场:Linux市场份额增至1.34%
· 做一个酷酷的在线编辑器(-)
· Ghost使用需要注意的三个问题
25870
43775
