国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > .NET > 正文

.NET 1.1中預編譯ASP.NET頁面實現原理淺析 [1] 自動預編譯機制淺析

2024-07-10 12:57:53
字體:
來源:轉載
供稿:網友
.net 1.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.net 2.0更是直接內置了預編譯asp.net頁面的功能。

實際上網上早就有人討論過在asp.net 1.1中模擬預編譯特性的實現方法,例如以下兩篇文章

pre-compiling asp.net web pages
pre-compile aspx pages in .net 1.1

其思路基本上都是遍歷所有需要預編譯的頁面文件,然后通過模擬web頁面請求的方式,觸發asp.net引擎的自動預編譯機制。這樣做的好處是完全模擬真實情況,無需了解asp.net引擎的實現原理;但同時也會受到諸多限制,如預編譯結果不透明,無法脫離原始asp.net頁面文件使用等等,而且無法使我們從原理上理解預編譯特性的實現。

下面我將分三到四個小節,簡要討論 asp.net 自動編譯機制的實現、asp.net 頁面文件編譯的實現以及如何在asp.net 1.1中實現手動預編譯頁面和相應分發機制。

[1] 自動預編譯機制淺析

本節我們將詳細分析討論.net 1.1中,asp.net引擎內部實現自動頁面預編譯的原理。

首先,我們所說的asp.net頁面實際上主要分為四類:

1.web 應用程序文件 global.asax
2.web 頁面文件 *.aspx
3.用戶自定義控件文件 *.ascx
4.web 服務程序文件 *.asmx

web 應用程序文件對于每個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頁面請求的調用發起者,偽代碼如下:


以下為引用:

public static void httpruntime.processrequest(httpworkerrequest wr)
{
// 檢查當前調用者有沒有作為asp.net宿主(host)的權限
internalsecuritypermissions.aspnethostingpermissionlevelmedium.demand();

if(wr == null)
{
throw new argumentnullexception("custom");
}

requestqueue queue = 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函數完成實際頁面請求工作,偽代碼如下:

以下為引用:

internal static void httpruntime.processrequestnow(httpworkerrequest wr)
{
httpruntime._theruntime.processrequestinternal(wr);
}




httpruntime.processrequestinternal函數邏輯稍微復雜一些,大致可分為四個部分。

首先檢查當前httpruntime實例是否第一次被調用,如果是第一次調用則通過firstrequestinit函數初始化;
接著調用httpresponse.initresponsewriter函數初始化頁面請求的返回對象httpworkerrequest.response;
然后調用httpapplicationfactory.getapplicationinstance函數獲取當前 web 應用程序實例;
最后使用web應用程序實例完成實際的頁面請求工作。

偽代碼如下:

以下為引用:

private void httpruntime.processrequestinternal(httpworkerrequest wr)
{
// 構造 http 調用上下文對象
httpcontext ctxt = new httpcontext(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 應用程序實例
ihttphandler handler = httpapplicationfactory.getapplicationinstance(ctxt);

if (handler == null)
{
throw new httpexception(httpruntime.formatresourcestring("unable_create_app_object"));
}

// 使用web應用程序實例完成實際的頁面請求工作
if((handler as ihttpasynchandler) != null)
{
ihttpasynchandler asynchandler = ((ihttpasynchandler) handler);
ctxt.asyncapphandler = asynchandler;
// 使用異步處理機制
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函數中,涉及到文件預編譯的有兩部分:一是獲取當前 web 應用程序實例時,會根據情況自動判斷是否預編譯web 應用程序文件;二是在完成實際頁面請求時,會在第一次使用某個頁面時觸發預編譯行為。

首先來看看對 web 應用程序文件的處理。

httpruntime.processrequestinternal函數中調用了httpapplicationfactory.getapplicationinstance函數獲取當前 web 應用程序實例。system.web.httpapplicationfactory是一個內部類,用以實現對多個web應用程序實例的管理和緩存。getapplicationinstance函數返回的是一個ihttphandler接口,提供ihttphandler.processrequest函數用于其后對web頁面文件的處理。偽代碼如下:

以下為引用:

internal static ihttphandler httpapplicationfactory.getapplicationinstance(httpcontext ctxt)
{
// 定制應用程序
if(httpapplicationfactory._customapplication != null)
{
return httpapplicationfactory._customapplication;
}
// 調試請求
if(httpdebughandler.isdebuggingrequest(ctxt))
{
return new httpdebughandler();
}

// 判斷是否需要初始化當前 httpapplicationfactory 實例
if(!httpapplicationfactory._theapplicationfactory._inited)
{
httpapplicationfactory factory = httpapplicationfactory._theapplicationfactory;

lock(httpapplicationfactory._theapplicationfactory);
{
// 使用 double-checked 模式 避免冗余鎖定
if(!httpapplicationfactory._theapplicationfactory._inited)
{
// 初始化當前 httpapplicationfactory 實例
httpapplicationfactory._theapplicationfactory.init(ctxt);
httpapplicationfactory._theapplicationfactory._inited = true;
}
}
}

// 獲取 web 應用程序實例
return httpapplicationfactory._theapplicationfactory.getnormalapplicationinstance(ctxt);
}




在處理特殊情況和可能的實例初始化之后,調用httpapplicationfactory.getnormalapplicationinstance函數完成獲取web應用程序實例的實際功能,偽代碼如下:

以下為引用:

private httpapplication httpapplicationfactory.getnormalapplicationinstance(httpcontext context)
{
httpapplication app = 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);
}

return app;
}




構造新的 web 應用程序實例的代碼很簡單,實際上就是對activator.createinstance函數的簡單包裝,偽代碼如下:

以下為引用:

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);
}




至此一個 web 應用程序實例就被完整構造出來,再經過initinternal函數的初始化,就可以開始實際頁面處理工作了。而httpapplicationfactory實例的_theapplicationtype類型,則是結果預編譯后的global.asax類。實際的預編譯工作在httpapplicationfactory.init函數中完成,偽代碼如下:

以下為引用:

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函數返回web請求物理目錄下的global.asax文件路徑;compileapplication函數則根據此文件是否存在,判斷是預編譯之并載入編譯后類型,還是直接返回缺省的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 應用程序類型
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();
}




分析到這里我們可以發現,內部類型system.web.ui.applicationfileparser的getcompiledapplicationtype函數是實際上進行web應用程序編譯工作的地方。但現在我們暫且打住,等下一節分析編譯過程時再詳細解說。 :)

然后我們看看對 web 頁面文件的處理。

在前面分析httpruntime.processrequestinternal函數時我們曾了解到,在獲得了web應用程序實例后,會使用此實例的ihttpasynchandler接口或ihttphandler接口,完成實際的頁面請求工作。而無論有否global.asax文件,最終返回的web應用程序實例都是一個httpapplication類或其子類的實例,其實現了ihttpasynchandler接口,支持異步的web頁面請求工作。對此接口的處理偽代碼如下:

以下為引用:

private void httpruntime.processrequestinternal(httpworkerrequest wr)
{
...

// 使用web應用程序實例完成實際的頁面請求工作
if((handler as ihttpasynchandler) != null)
{
ihttpasynchandler asynchandler = ((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接口,偽代碼如下:

以下為引用:

void httpapplication.processrequest(system.web.httpcontext context)
{
throw new httpexception(httpruntime.formatresourcestring("sync_not_supported"));
}

bool httpapplication.get_isreusable()
{
return true;
}




而在httpapplication.ihttpasynchandler.beginprocessrequest函數中,將完成非常復雜的異步調用后臺處理操作,這兒就不多羅嗦了,等有機會寫篇文章專門討論一下asp.net中的異步操作再說。而其最終調用還是使用system.web.ui.pageparser對需要處理的web頁面進行解析和編譯。

最后我們看看對用戶自定義控件文件的處理。

page類的loadcontrol函數實際上是在抽象類templatecontrol中實現的,偽代碼如下:

以下為引用:

public control loadcontrol(string virtualpath)
{
virtualpath = urlpath.combine(base.templatesourcedirectory, virtualpath);
type type = usercontrolparser.getcompiledusercontroltype(virtualpath, null, base.context);
return this.loadcontrol(type1);
}




實際的用戶自定義控件預編譯操作還是在usercontrolparser類中完成的。

至此,在這一節中我們已經大致了解了asp.net自動預編譯的實現原理,以及在什么時候對頁面文件進行預編譯。下一節我們將詳細分析applicationfileparser、pageparser和usercontrolparser,了解asp.net是如何對頁面文件進行預編譯的。

商業源碼熱門下載www.html.org.cn

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 宁海县| 晋州市| 兴隆县| 姜堰市| 靖宇县| 通州区| 独山县| 崇阳县| 兰州市| 仪征市| 光泽县| 报价| 温宿县| 乳山市| 玉环县| 建水县| 蓬溪县| 常宁市| 黄平县| 怀远县| 富源县| 南汇区| 天门市| 宁陵县| 崇礼县| 德清县| 荆门市| 黔西县| 比如县| 万山特区| 靖西县| 谷城县| 长沙县| 霸州市| 施甸县| 永平县| 安西县| 库尔勒市| 台江县| 孟津县| 宁国市|