.NET1.1中预编译ASP.NET页面实现原理浅析[1]自动预编译机制浅析
                                                            生活随笔
收集整理的這篇文章主要介紹了
                                .NET1.1中预编译ASP.NET页面实现原理浅析[1]自动预编译机制浅析
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.                        
                                .NET1.1中預(yù)編譯ASP.NET頁(yè)面實(shí)現(xiàn)原理淺析[1]自動(dòng)預(yù)編譯機(jī)制淺析
.NET1.1中預(yù)編譯ASP.NET頁(yè)面實(shí)現(xiàn)原理淺析[1]自動(dòng)預(yù)編譯機(jī)制淺析作者:&;nbsp來(lái)自:網(wǎng)絡(luò)
http://flier_lu.blogone.net?id=1544105?
.net 1.1中預(yù)編譯asp.net頁(yè)面實(shí)現(xiàn)原理淺析
? ? MS在發(fā)布ASP.NET時(shí)的一大功能特性是,與ASP和PHP等腳本語(yǔ)言不同,ASP.NET實(shí)際上是一種編譯型的快速網(wǎng)頁(yè)開(kāi)發(fā)環(huán)境。這使得ASP.NET在具有開(kāi)發(fā)和修改的簡(jiǎn)便性的同時(shí),不會(huì)負(fù)擔(dān)效率方面的損失。實(shí)現(xiàn)上ASP.NET與JSP的思路類似,引擎在第一次使用一個(gè)頁(yè)面之前,會(huì)將之編譯成一個(gè)類,自動(dòng)生成Assembly并載入執(zhí)行。
? ? 而通過(guò)《在WinForm程序中嵌入ASP.NET》一文中我們可以了解到,ASP.NET引擎實(shí)際上是可以無(wú)需通過(guò)IIS等Web服務(wù)器調(diào)用而被使用的,這就使得手工預(yù)編譯ASP.NET頁(yè)面成為可能。實(shí)際上這個(gè)需求是普遍存在的,早在ASP時(shí)代就層有第三方產(chǎn)品支持將ASP頁(yè)面編譯成二進(jìn)制程序,以提高執(zhí)行效率和保障代碼安全性,而將伴隨Whidbey發(fā)布的ASP.NET 2.0更是直接內(nèi)置了預(yù)編譯ASP.NET頁(yè)面的功能。
? ? 實(shí)際上網(wǎng)上早就有人討論過(guò)在ASP.NET 1.1中模擬預(yù)編譯特性的實(shí)現(xiàn)方法,例如以下兩篇文章
? ? Pre-Compiling ASP.NET Web Pages
? ? Pre-Compile ASPX pages in .NET 1.1
? ? 其思路基本上都是遍歷所有需要預(yù)編譯的頁(yè)面文件,然后通過(guò)模擬Web頁(yè)面請(qǐng)求的方式,觸發(fā)ASP.NET引擎的自動(dòng)預(yù)編譯機(jī)制。這樣做的好處是完全模擬真實(shí)情況,無(wú)需了解ASP.NET引擎的實(shí)現(xiàn)原理;但同時(shí)也會(huì)受到諸多限制,如預(yù)編譯結(jié)果不透明,無(wú)法脫離原始ASP.NET頁(yè)面文件使用等等,而且無(wú)法使我們從原理上理解預(yù)編譯特性的實(shí)現(xiàn)。
? ? 下面我將分三到四個(gè)小節(jié),簡(jiǎn)要討論 ASP.NET 自動(dòng)編譯機(jī)制的實(shí)現(xiàn)、ASP.NET 頁(yè)面文件編譯的實(shí)現(xiàn)以及如何在ASP.NET 1.1中實(shí)現(xiàn)手動(dòng)預(yù)編譯頁(yè)面和相應(yīng)分發(fā)機(jī)制。
[1] 自動(dòng)預(yù)編譯機(jī)制淺析
? ? 本節(jié)我們將詳細(xì)分析討論.NET 1.1中,ASP.NET引擎內(nèi)部實(shí)現(xiàn)自動(dòng)頁(yè)面預(yù)編譯的原理。
? ? 首先,我們所說(shuō)的ASP.NET頁(yè)面實(shí)際上主要分為四類:
? ? 1.Web 應(yīng)用程序文件? ? Global.asax
? ? 2.Web 頁(yè)面文件? ? ? ? *.aspx
? ? 3.用戶自定義控件文件? *.ascx
? ? 4.Web 服務(wù)程序文件? ? *.asmx
? ? Web 應(yīng)用程序文件對(duì)于每個(gè)Web 應(yīng)用程序來(lái)說(shuō)是可選唯一的,用來(lái)處理ASP.NET應(yīng)用程序一級(jí)的事件,并將被預(yù)編譯為一個(gè)System.Web.HttpApplication類的子類;
? ? Web 頁(yè)面文件是普通的ASP.NET頁(yè)面,處理特定頁(yè)面的事件,將被預(yù)編譯為一個(gè)System.Web.UI.Page類的子類;
? ? 用戶自定義控件文件是特殊的ASP.NET頁(yè)面,處理控件自身的事件,將被預(yù)編譯為一個(gè)System.Web.UI.UserControl類的子類;
? ? Web 服務(wù)程序文件則是與前三者不太相同的一種特殊頁(yè)面文件,暫時(shí)不予討論。
? ? 然后,前三種ASP.NET文件的編譯時(shí)機(jī)也不完全相同。Web 應(yīng)用程序文件在此 Web 應(yīng)用程序文件第一次被使用時(shí)自動(dòng)編譯;Web 頁(yè)面文件在此Web頁(yè)面第一次被使用時(shí)自動(dòng)編譯,實(shí)際上是調(diào)用 HttpRuntime.ProcessRequest 函數(shù)觸發(fā)預(yù)編譯;用戶自定義控件文件則在其第一次被 Web 頁(yè)面使用的時(shí)候自動(dòng)編譯,實(shí)際上是調(diào)用 Page.LoadControl 函數(shù)觸發(fā)預(yù)編譯。
? ? 在了解了以上這些基本知識(shí)后,我們來(lái)詳細(xì)分析一下自動(dòng)預(yù)編譯的實(shí)現(xiàn)機(jī)制。
? ? HttpRuntime.ProcessRequest 函數(shù)是處理Web頁(yè)面請(qǐng)求的調(diào)用發(fā)起者,偽代碼如下:
?
以下為引用:
public static void HttpRuntime.ProcessRequest(HttpWorkerRequest wr)
{
? // 檢查當(dāng)前調(diào)用者有沒(méi)有作為ASP.NET宿主(Host)的權(quán)限
? InternalSecurityPermissions.AspNetHostingPermissionLevelMedium.Demand();
? if(wr == null)
? {
? ? throw new ArgumentNullException("custom");
? }
? RequestQueue queue = HttpRuntime._theRuntime._requestQueue;
? if(queue != null)
? {
? ? // 將參數(shù)中的Web頁(yè)面請(qǐng)求放入請(qǐng)求隊(duì)列中
? ? // 并從隊(duì)列中使用FIFO策略獲取一個(gè)頁(yè)面請(qǐng)求
? ? wr = queue.GetRequestToExecute(wr);
? }
? if(wr != null)
? {
? ? // 更新性能計(jì)數(shù)器
? ? HttpRuntime.CalculateWaitTimeAndUpdatePerfCounter(wr);
? ? // 實(shí)際完成頁(yè)面請(qǐng)求工作
? ? HttpRuntime.ProcessRequestNow(wr);
? }
}
? ? HttpRuntime.ProcessRequestNow函數(shù)則直接調(diào)用缺省HttpRuntime實(shí)例的ProcessRequestInternal函數(shù)完成實(shí)際頁(yè)面請(qǐng)求工作,偽代碼如下:
以下為引用:
internal static void HttpRuntime.ProcessRequestNow(HttpWorkerRequest wr)
{
? HttpRuntime._theRuntime.ProcessRequestInternal(wr);
}
? ? HttpRuntime.ProcessRequestInternal函數(shù)邏輯稍微復(fù)雜一些,大致可分為四個(gè)部分。
? ? 首先檢查當(dāng)前HttpRuntime實(shí)例是否第一次被調(diào)用,如果是第一次調(diào)用則通過(guò)FirstRequestInit函數(shù)初始化;
? ? 接著調(diào)用HttpResponse.InitResponseWriter函數(shù)初始化頁(yè)面請(qǐng)求的返回對(duì)象HttpWorkerRequest.Response;
? ? 然后調(diào)用HttpApplicationFactory.GetApplicationInstance函數(shù)獲取當(dāng)前 Web 應(yīng)用程序?qū)嵗?#xff1b;
? ? 最后使用Web應(yīng)用程序?qū)嵗瓿蓪?shí)際的頁(yè)面請(qǐng)求工作。
? ? 偽代碼如下:
以下為引用:
private void HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr)
{
? // 構(gòu)造 HTTP 調(diào)用上下文對(duì)象
? HttpContext ctxt = new HttpContext(wr, 0);
? // 設(shè)置發(fā)送結(jié)束異步回調(diào)函數(shù)
? wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, ctxt);
? // 更新請(qǐng)求計(jì)數(shù)器
? Interlocked.Increment(&;amp;(this._activeRequestCount));
? try
? {
? ? // 檢查當(dāng)前HttpRuntime實(shí)例是否第一次被調(diào)用
? ? if(this._beforeFirstRequest)
? ? {
? ? ? lock(this)
? ? ? {
? ? ? ? // 使用 Double-Checked 模式 避免冗余鎖定
? ? ? ? if(this._beforeFirstRequest)
? ? ? ? {
? ? ? ? ? this._firstRequestStartTime = DateTime.UtcNow;
? ? ? ? ? this.FirstRequestInit(ctxt); // 初始化當(dāng)前 HttpRuntime 運(yùn)行時(shí)環(huán)境
? ? ? ? ? this._beforeFirstRequest = false;
? ? ? ? }
? ? ? }
? ? }
? ? // 根據(jù)配置文件設(shè)置,扮演具有較高特權(quán)的角色
? ? ctxt.Impersonation.Start(true, false);
? ? try
? ? {
? ? ? // 初始化頁(yè)面請(qǐng)求的返回對(duì)象
? ? ? ctxt.Response.InitResponseWriter();
? ? }
? ? finally
? ? {
? ? ? ctxt.Impersonation.Stop();
? ? }
? ? // 獲取當(dāng)前 Web 應(yīng)用程序?qū)嵗?br />? ? IHttpHandler handler = HttpApplicationFactory.GetApplicationInstance(ctxt);
? ? if (handler == null)
? ? {
? ? ? throw new HttpException(HttpRuntime.FormatResourceString("Unable_create_app_object"));
? ? }
? ? // 使用Web應(yīng)用程序?qū)嵗瓿蓪?shí)際的頁(yè)面請(qǐng)求工作
? ? if((handler as IHttpAsyncHandler) != null)
? ? {
? ? ? IHttpAsyncHandler asyncHandler = ((IHttpAsyncHandler) handler);
? ? ? ctxt.AsyncAppHandler = asyncHandler;
? ? ? // 使用異步處理機(jī)制
? ? ? asyncHandler.BeginProcessRequest(ctxt, this._handlerCompletionCallback, ctxt);
? ? }
? ? else
? ? {
? ? ? handler.ProcessRequest(ctxt);
? ? ? this.FinishRequest(ctxt.WorkerRequest, ctxt, null);
? ? }
? }
? catch(Exception E)
? {
? ? ctxt.Response.InitResponseWriter();
? ? this.FinishRequest(wr, ctxt, E);
? }
}
? ? HttpRuntime.ProcessRequestInternal函數(shù)中,涉及到文件預(yù)編譯的有兩部分:一是獲取當(dāng)前 Web 應(yīng)用程序?qū)嵗龝r(shí),會(huì)根據(jù)情況自動(dòng)判斷是否預(yù)編譯Web 應(yīng)用程序文件;二是在完成實(shí)際頁(yè)面請(qǐng)求時(shí),會(huì)在第一次使用某個(gè)頁(yè)面時(shí)觸發(fā)預(yù)編譯行為。
? ? 首先來(lái)看看對(duì) Web 應(yīng)用程序文件的處理。
? ? HttpRuntime.ProcessRequestInternal函數(shù)中調(diào)用了HttpApplicationFactory.GetApplicationInstance函數(shù)獲取當(dāng)前 Web 應(yīng)用程序?qū)嵗ystem.Web.HttpApplicationFactory是一個(gè)內(nèi)部類,用以實(shí)現(xiàn)對(duì)多個(gè)Web應(yīng)用程序?qū)嵗墓芾砗途彺妗etApplicationInstance函數(shù)返回的是一個(gè)IHttpHandler接口,提供IHttpHandler.ProcessRequest函數(shù)用于其后對(duì)Web頁(yè)面文件的處理。偽代碼如下:
以下為引用:
internal static IHttpHandler HttpApplicationFactory.GetApplicationInstance(HttpContext ctxt)
{
? // 定制應(yīng)用程序
? if(HttpApplicationFactory._customApplication != null)
? {
? ? return HttpApplicationFactory._customApplication;
? }
? // 調(diào)試請(qǐng)求
? if(HttpDebugHandler.IsDebuggingRequest(ctxt))
? {
? ? return new HttpDebugHandler();
? }
? // 判斷是否需要初始化當(dāng)前 HttpApplicationFactory 實(shí)例
? if(!HttpApplicationFactory._theApplicationFactory._inited)
? {
? ? HttpApplicationFactory factory = HttpApplicationFactory._theApplicationFactory;
? ? lock(HttpApplicationFactory._theApplicationFactory);
? ? {
? ? ? // 使用 Double-Checked 模式 避免冗余鎖定
? ? ? if(!HttpApplicationFactory._theApplicationFactory._inited)
? ? ? {
? ? ? ? // 初始化當(dāng)前 HttpApplicationFactory 實(shí)例
? ? ? ? HttpApplicationFactory._theApplicationFactory.Init(ctxt);
? ? ? ? HttpApplicationFactory._theApplicationFactory._inited = true;
? ? ? }
? ? }
? }
? // 獲取 Web 應(yīng)用程序?qū)嵗?br />? return HttpApplicationFactory._theApplicationFactory.GetNormalApplicationInstance(ctxt);
}
? ? 在處理特殊情況和可能的實(shí)例初始化之后,調(diào)用HttpApplicationFactory.GetNormalApplicationInstance函數(shù)完成獲取Web應(yīng)用程序?qū)嵗膶?shí)際功能,偽代碼如下:
以下為引用:
private HttpApplication HttpApplicationFactory.GetNormalApplicationInstance(HttpContext context)
{
? HttpApplication app = null;
? // 嘗試從已施放的 Web 應(yīng)用程序?qū)嵗?duì)列中獲取
? lock(this._freeList)
? {
? ? if(this._numFreeAppInstances > 0)
? ? {
? ? ? app = (HttpApplication)this._freeList.Pop();
? ? ? this._numFreeAppInstances--;
? ? }
? }
? if(app == null)
? {
? ? // 構(gòu)造新的 Web 應(yīng)用程序?qū)嵗?br />? app = (HttpApplication)System.Web.HttpRuntime.CreateNonPublicInstance(this._theApplicationType);
? // 初始化 Web 應(yīng)用程序?qū)嵗?br />app.InitInternal(context, this._state, this._eventHandlerMethods);
? }
? return app;
}
? ? 構(gòu)造新的 Web 應(yīng)用程序?qū)嵗拇a很簡(jiǎn)單,實(shí)際上就是對(duì)Activator.CreateInstance函數(shù)的簡(jiǎn)單包裝,偽代碼如下:
以下為引用:
internal static object HttpRuntime.CreateNonPublicInstance(Type type, object[] args)
{
? return Activator.CreateInstance(type, BindingFlags.CreateInstance | BindingFlags.Instance |
? ? BindingFlags.NonPublic | BindingFlags.Public, null, args, null);
}
internal static object HttpRuntime.CreateNonPublicInstance(Type type)
{
? return HttpRuntime.CreateNonPublicInstance(type, null);
}
? ? 至此一個(gè) Web 應(yīng)用程序?qū)嵗捅煌暾麡?gòu)造出來(lái),再經(jīng)過(guò)InitInternal函數(shù)的初始化,就可以開(kāi)始實(shí)際頁(yè)面處理工作了。而HttpApplicationFactory實(shí)例的_theApplicationType類型,則是結(jié)果預(yù)編譯后的Global.asax類。實(shí)際的預(yù)編譯工作在HttpApplicationFactory.Init函數(shù)中完成,偽代碼如下:
以下為引用:
private void HttpApplicationFactory.Init(HttpContext ctxt)
{
if(HttpApplicationFactory._customApplication != null)
return;
? using(HttpContextWrapper wrapper = new HttpContextWrapper(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函數(shù)返回Web請(qǐng)求物理目錄下的global.asax文件路徑;CompileApplication函數(shù)則根據(jù)此文件是否存在,判斷是預(yù)編譯之并載入編譯后類型,還是直接返回缺省的HttpApplication類型,偽代碼如下:
以下為引用:
internal static string HttpApplicationFactory.GetApplicationFile(HttpContext ctxt)
{
? return Path.Combine(ctxt.Request.PhysicalApplicationPath, "global.asax");
}
private void HttpApplicationFactory.CompileApplication(HttpContext ctxt)
{
? if(FileUtil.FileExists(this._appFilename))
? {
? ? ApplicationFileParser parser;
? ? // 獲取編譯后的 Web 應(yīng)用程序類型
? ? this._theApplicationType = ApplicationFileParser.GetCompiledApplicationType(this._appFilename, context, out parser);
? ? this._state = new HttpApplicationState(parser1.ApplicationObjects, parser.SessionObjects);
? ? this._fileDependencies = parser.SourceDependencies;
? }
? else
? {
? ? this._theApplicationType = typeof(HttpApplication);
? ? this._state = new HttpApplicationState();
? }
? this.ReflectOnApplicationType();
}
? ? 分析到這里我們可以發(fā)現(xiàn),內(nèi)部類型System.Web.UI.ApplicationFileParser的GetCompiledApplicationType函數(shù)是實(shí)際上進(jìn)行Web應(yīng)用程序編譯工作的地方。但現(xiàn)在我們暫且打住,等下一節(jié)分析編譯過(guò)程時(shí)再詳細(xì)解說(shuō)。 :)
? ? 然后我們看看對(duì) Web 頁(yè)面文件的處理。
? ? 在前面分析HttpRuntime.ProcessRequestInternal函數(shù)時(shí)我們?cè)私獾?#xff0c;在獲得了Web應(yīng)用程序?qū)嵗?#xff0c;會(huì)使用此實(shí)例的IHttpAsyncHandler接口或IHttpHandler接口,完成實(shí)際的頁(yè)面請(qǐng)求工作。而無(wú)論有否Global.asax文件,最終返回的Web應(yīng)用程序?qū)嵗际且粋€(gè)HttpApplication類或其子類的實(shí)例,其實(shí)現(xiàn)了IHttpAsyncHandler接口,支持異步的Web頁(yè)面請(qǐng)求工作。對(duì)此接口的處理偽代碼如下:
以下為引用:
private void HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr)
{
? ...
? // 使用Web應(yīng)用程序?qū)嵗瓿蓪?shí)際的頁(yè)面請(qǐng)求工作
? if((handler as IHttpAsyncHandler) != null)
? {
? ? IHttpAsyncHandler asyncHandler = ((IHttpAsyncHandler) handler);
? ? ctxt.AsyncAppHandler = asyncHandler;
? ? // 使用異步處理機(jī)制
? ? asyncHandler.BeginProcessRequest(ctxt, this._handlerCompletionCallback, ctxt);
? }
? else
? {
? ? handler.ProcessRequest(ctxt);
? ? this.FinishRequest(ctxt.WorkerRequest, ctxt, null);
? }
? ...
}
? ? HttpRuntime.ProcessRequestInternal函數(shù)通過(guò)調(diào)用HttpApplication.IHttpAsyncHandler.BeginProcessRequest函數(shù)開(kāi)始頁(yè)面請(qǐng)求工作。而HttpApplication實(shí)際上根本不支持同步形式的IHttpHandler接口,偽代碼如下:
以下為引用:
void HttpApplication.ProcessRequest(System.Web.HttpContext context)
{
? throw new HttpException(HttpRuntime.FormatResourceString("Sync_not_supported"));
}
bool HttpApplication.get_IsReusable()
{
? return true;
}
? ? 而在HttpApplication.IHttpAsyncHandler.BeginProcessRequest函數(shù)中,將完成非常復(fù)雜的異步調(diào)用后臺(tái)處理操作,這兒就不多羅嗦了,等有機(jī)會(huì)寫(xiě)篇文章專門討論一下ASP.NET中的異步操作再說(shuō)。而其最終調(diào)用還是使用System.Web.UI.PageParser對(duì)需要處理的Web頁(yè)面進(jìn)行解析和編譯。
? ? 最后我們看看對(duì)用戶自定義控件文件的處理。
? ? Page類的LoadControl函數(shù)實(shí)際上是在抽象類TemplateControl中實(shí)現(xiàn)的,偽代碼如下:
以下為引用:
public Control LoadControl(string virtualPath)
{
? virtualPath = UrlPath.Combine(base.TemplateSourceDirectory, virtualPath);
? Type type = UserControlParser.GetCompiledUserControlType(virtualPath, null, base.Context);
? return this.LoadControl(type1);
}
? ? 實(shí)際的用戶自定義控件預(yù)編譯操作還是在UserControlParser類中完成的。
? ? 至此,在這一節(jié)中我們已經(jīng)大致了解了ASP.NET自動(dòng)預(yù)編譯的實(shí)現(xiàn)原理,以及在什么時(shí)候?qū)?yè)面文件進(jìn)行預(yù)編譯。下一節(jié)我們將詳細(xì)分析ApplicationFileParser、PageParser和UserControlParser,了解ASP.NET是如何對(duì)頁(yè)面文件進(jìn)行預(yù)編譯的。
轉(zhuǎn)載于:https://www.cnblogs.com/danhuoren/archive/2008/08/23/1274761.html
總結(jié)
以上是生活随笔為你收集整理的.NET1.1中预编译ASP.NET页面实现原理浅析[1]自动预编译机制浅析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
 
                            
                        - 上一篇: 【转】如何调整一个 IFrame 到其内
- 下一篇: 最短路径问题的算法实现【转载】
