|
一、服務(wù)器腳本基礎(chǔ)介紹
首先,我們先復(fù)習(xí)一下Web服務(wù)器頁面的基本執(zhí)行方式:
1、客戶端通過在瀏覽器的地址欄敲入地址來發(fā)送請求到服務(wù)器端
2、服務(wù)器接收到請求之后,發(fā)給相應(yīng)的服務(wù)器端頁面(也就是腳本)來執(zhí)行,腳本產(chǎn)生客戶端的響應(yīng),發(fā)送回客戶端
3、客戶端瀏覽器接收到服務(wù)器傳回的響應(yīng),對Html進(jìn)行解析,將圖形化的網(wǎng)頁呈現(xiàn)在用戶面前
對于服務(wù)器和客戶端的交互,通常通過下面幾種主要方式:
1、Form:這是最主要的方式,標(biāo)準(zhǔn)化的控件來獲取用戶的輸入,F(xiàn)orm的提交將數(shù)據(jù)發(fā)送給服務(wù)器端處理
2、QueryString:通過在Url后面帶參數(shù)達(dá)到將參數(shù)傳送給服務(wù)器,這種方式其實(shí)跟Get方式的Form是一樣的
3、Cookies:這是一種比較特殊的方式,通常用于用戶身份的確認(rèn)
二、ASP.NET簡介
傳統(tǒng)的服務(wù)器腳本語言,如ASP、JSP等,編寫服務(wù)器腳本的方式大同小異,都是在Html中嵌入解釋或編譯執(zhí)行的代碼,由服務(wù)器平臺執(zhí)行這些代碼來生成Html;對于這類似的腳本,頁面的生存周期實(shí)際上很簡單,就是從開頭至末尾,執(zhí)行完所有的代碼,當(dāng)然用Java編寫的Servlet可以編寫更復(fù)雜的代碼,但是從結(jié)構(gòu)上看,和JSP沒什么區(qū)別。
ASP.NET的出現(xiàn),打破了這種傳統(tǒng);ASP.NET采用了CodeBehind技術(shù)和服務(wù)器端控件,加入了服務(wù)器端的事件的概念,改變了腳本語言編寫的模式,更加貼近Window編程,使Web編程更加簡單、直觀;但是我們要看到,ASP.NET本身并沒有改變Web編程的基本模式,只是封裝了一些細(xì)節(jié)、提供了一些易用的功能,使代碼更容易編寫和維護(hù);從某種程度上來說,將服務(wù)器端執(zhí)行的方式復(fù)雜化了,這就是我們今天要討論的主體:ASP.NET Web Page的生存周期。
三、ASP.NET請求處理模式
我們說,ASP.NET的Web Page并沒有脫離Web編程的模式,所以它仍然是以 請求->接收請求->處理請求->發(fā)送響應(yīng) 這樣的模式在工作,每一次與客戶端的交互都會引發(fā)一次新的請求,所以一個Web Page的生命周期是以一次請求為基礎(chǔ)的。
當(dāng)IIS收到客戶端的請求的時候,會將請求交給ASPNET_wp這個進(jìn)程來處理,這個進(jìn)程會查看請求的應(yīng)用程序域是否存在,如果不存在則會創(chuàng)建一個,然后會創(chuàng)建一個Http運(yùn)行時(HttpRuntime)來處理請求,這個運(yùn)行時“為當(dāng)前應(yīng)用程序提供一組 ASP.NET 運(yùn)行時服務(wù)”(摘自MSDN)。
HttpRuntime在處理請求的時候,會維護(hù)一系列的應(yīng)用程序?qū)嵗簿褪?a href=/pingce/yingyong/ target=_blank class=infotextkey>應(yīng)用程序的Global類(global.asax)的實(shí)例,這些實(shí)例在沒有請求的時候,會存放在一個應(yīng)用程序池中(實(shí)際上應(yīng)用程序池由另一個類來維護(hù),HttpRuntime只是簡單的調(diào)用),每接收到一個請求,HttpRuntime都會獲取一個閑置的實(shí)例來處理請求,這個實(shí)例在請求結(jié)束前不會處理其他的請求,處理完畢之后,它又會回到池中,“一個實(shí)例在其生存期內(nèi)被用于處理多個請求,但它一次只能處理一個請求。”(摘自MSDN)
當(dāng)應(yīng)用程序?qū)嵗幚碚埱蟮臅r候,它會創(chuàng)建請求頁面類的實(shí)例,執(zhí)行它的ProcessRequest方法來處理請求,這個方法也就是Web Page生命周期的開始。
四、ASPx頁面與CodeBehind
在深入了解頁面的生命周期之前,我們先來探討一些ASPx與CodeBehind之間的關(guān)系。
<%@ Page language="c#" Codebehind="WebForm.ASPx.cs" Inherits="MyNamespace.WebForm" %>
相信使用過CodeBehind技術(shù)的朋友,對ASPX頂部的這句話應(yīng)該是非常熟悉了,我們來一項(xiàng)一項(xiàng)的分析它:
Page language="c#" 這個就不用多說了吧
Codebehind="WebForm.ASPx.cs" 這一句表示綁定的代碼文件
Inherits="MyNamespace.WebForm" 這句非常重要,它表示頁面繼承的類名稱,也就是CodeBehind的代碼文件中的類,這個類必須從System.Web.WebControls.Page派生
從上面我們可以分析出,實(shí)際上CodeBehind中的類就是頁面(ASPX)的基類,到這里,可能有些朋友要問了,在編寫ASPX的時候,完全是按照ASP的方式,在Html中嵌入代碼或者嵌入服務(wù)器控件,沒有看到所謂“類”的影子啊?
這個問題實(shí)際上并不復(fù)雜,各位使用ASP.NET編程的朋友可以到你們的系統(tǒng)盤:/WINDOWS/Microsoft.NET/Framework/<版本號>/Temporary ASP.NET Files這個目錄下,這個下面就放了所有本機(jī)上存在的ASP.NET應(yīng)用程序的臨時文件,子目錄的名稱就是應(yīng)用程序的名稱,然后再下去兩層(為了保證唯一,ASP.NET自動產(chǎn)生了兩層子目錄,并且子目錄名稱是隨機(jī)的),然后我們會發(fā)現(xiàn)有很多類似:“yfy1gjhc.dll”、“xeunj5u3.dll”這樣的鏈接庫以及“komee-bp.0.cs”、“9falckav.0.cs”這樣的源文件,實(shí)際上這就是ASPX被ASP.NET動態(tài)編譯后的結(jié)果,打開這些源文件我們可以發(fā)現(xiàn):
public class WebForm_ASPx : MyNamespace.WebForm, System.Web.SessionState.IRequiresSessionState
這就印證了我們前面的說法,ASPX是代碼綁定類的子類,它的名稱是ASPX文件名加上“_ASPx”后綴,通過研究這些代碼我們可以發(fā)現(xiàn),實(shí)際上所有ASPx中定義的服務(wù)器控件都是在這些代碼中生成的,然后動態(tài)產(chǎn)生這些代碼的時候,把原來在ASPX中嵌入的代碼寫在了相應(yīng)的位置。
當(dāng)某個頁面第一次被訪問的時候,Http運(yùn)行時就會使用一個代碼生成器去解析ASPX文件并生成源代碼并編譯,然后以后的訪問就直接調(diào)用編譯后的dll,這也是為什么ASPX第一次訪問的時候非常慢的原因。
解釋了這個問題,我們再來看另一個問題。我們在使用代碼綁定的時候,在設(shè)計(jì)頁面拖一個控件,然后切換到代碼視圖,就可以直接在Page_Load中使用這個控件了,既然控件是在子類中產(chǎn)生的,那為什么在父類中可以直接使用呢?
實(shí)際上我們可以發(fā)現(xiàn),每當(dāng)用VS.NET拖一個控件到頁面上,代碼綁定文件中總是會類似這樣的添加一個聲明:
protected System.Web.WebControls.Button Button1;
我們可以發(fā)現(xiàn)這個字段被聲明成protected,而且名字與ASPX中控件的ID一致,仔細(xì)想一想,這個問題就迎刃而解了。我們前面提到ASPX的源代碼是被生成器動態(tài)生成和編譯的,生成器會產(chǎn)生動態(tài)生成每一個服務(wù)器控件的代碼,在生成的時候,它會檢查父類有沒有聲明這個控件,如果聲明了,它會添加類似下面的一句代碼:
this.DataGrid1 = __ctrl;
這個__ctrl就是生成該控件的變量,這時候它就把控件的引用賦給了父類中相應(yīng)的變量,這也是為什么父類中的聲明必須為protected(實(shí)際上也可以為public),因?yàn)橐WC子類能夠調(diào)用。
然后在執(zhí)行Page_Load的時候,因?yàn)檫@時候父類的聲明已經(jīng)被子類中的初始化代碼賦了值,所以我們就可以使用這個字段來訪問對應(yīng)的控件,了解了這些,我們就不會犯在代碼綁定文件中的構(gòu)造器里使用控件,造成空引用的異常的錯誤了,因?yàn)闃?gòu)造器是最先執(zhí)行的,這時候子類的初始化還沒有開始,所以父類中的字段是空值,至于子類是什么時候初始化我們放到后面討論。
五、頁面生存周期
現(xiàn)在回到第三個標(biāo)題中講到的內(nèi)容,我們講到了HttpApplication的實(shí)例接收請求,并創(chuàng)建頁面類的實(shí)例,實(shí)際上這個實(shí)例也就是動態(tài)編譯的ASPX的類的一個實(shí)例,上一個標(biāo)題中我們了解到ASPX實(shí)際上是代碼綁定中類的子類,所以它繼承了所有的protected方法。
現(xiàn)在我們來看看VS.NET自動生成的CodeBehind類的代碼,以此來開始我們對頁面生命周期的探討:
復(fù)制代碼 代碼如下:
#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
//
// CODEGEN:該調(diào)用是 ASP.NET Web 窗體設(shè)計(jì)器所必需的。
//
InitializeComponent();
base.OnInit(e);
}
/// <summary>
/// 設(shè)計(jì)器支持所需的方法 - 不要使用代碼編輯器修改
/// 此方法的內(nèi)容。
/// </summary>
private void InitializeComponent()
{
this.DataGrid1.ItemDataBound += new System.Web.UI.WebControls.DataGridItemEventHandler(this.DataGrid1_ItemDataBound);
this.Load += new System.EventHandler(this.Page_Load);
}
#endregion
這個就是使用VS.NET產(chǎn)生的Page的代碼,我們來看,這里面有兩個方法,一個是OnInit,一個是InitializeComponent,后者被前者調(diào)用,實(shí)際上這就是頁面初始化的開始,在InitializeComponent中我們看到了控件的事件聲明和Page的Load聲明。
下面是從MSDN中摘錄的一段描述和一個頁面生命周期方法和事件觸發(fā)的順序表:
“每次請求 ASP.NET 頁時,服務(wù)器就會加載一個 ASP.NET 頁,并在請求完成時卸載該頁。頁及其包含的服務(wù)器控件負(fù)責(zé)執(zhí)行請求并將 HTML 呈現(xiàn)給客戶端。雖然客戶端和服務(wù)器之間的通訊是無狀態(tài)的和斷續(xù)的,但是必須使客戶感覺到這是一個連續(xù)執(zhí)行的過程。”
“這種連續(xù)性假象是由 ASP.NET 頁框架、頁及其控件實(shí)現(xiàn)的。回發(fā)后,控件的行為必須看起來是從上次 Web 請求結(jié)束的地方開始的。雖然 ASP.NET 頁框架可使執(zhí)行狀態(tài)管理相對容易一些,但是為了獲得連續(xù)性效果,控件開發(fā)人員必須知道控件的執(zhí)行順序。控件開發(fā)人員需要了解:在控件生命周期的各個階段,控件可使用哪些信息、保持哪些數(shù)據(jù)、控件呈現(xiàn)時處于哪種狀態(tài)。例如,在填充頁上的控件樹之前控件不能調(diào)用其父級。” “下表提供了控件生命周期中各階段的高級概述。有關(guān)詳細(xì)信息,請點(diǎn)擊表中的鏈接。”
階段 控件需要執(zhí)行的操作 要重寫的方法或事件
初始化 初始化在傳入 Web 請求生命周期內(nèi)所需的設(shè)置。請參閱處理繼承的事件。 Init 事件(OnInit 方法)
加載視圖狀態(tài) 在此階段結(jié)束時,就會自動填充控件的 ViewState 屬性,詳見維護(hù)控件中的狀態(tài)中的介紹。控件可以重寫 LoadViewState 方法的默認(rèn)實(shí)現(xiàn),以自定義狀態(tài)還原。 LoadViewState 方法
處理回發(fā)數(shù)據(jù) 處理傳入窗體數(shù)據(jù),并相應(yīng)地更新屬性。請參閱處理回發(fā)數(shù)據(jù)。
注意 只有處理回發(fā)數(shù)據(jù)的控件參與此階段。 LoadPostData 方法 (如果已實(shí)現(xiàn)IPostBackDataHandler)
加載 執(zhí)行所有請求共有的操作,如設(shè)置數(shù)據(jù)庫查詢。此時,樹中的服務(wù)器控件已創(chuàng)建并初始化、狀態(tài)已還原并且窗體控件反映了客戶端的數(shù)據(jù)。請參閱處理繼承的事件。 Load 事件
(OnLoad 方法)
發(fā)送回發(fā)更改通知 引發(fā)更改事件以響應(yīng)當(dāng)前和以前回發(fā)之間的狀態(tài)更改。請參閱處理回發(fā)數(shù)據(jù)。
注意 只有引發(fā)回發(fā)更改事件的控件參與此階段。 RaisePostDataChangedEvent 方法
(如果已實(shí)現(xiàn) IPostBackDataHandler)
處理回發(fā)事件 處理引起回發(fā)的客戶端事件,并在服務(wù)器上引發(fā)相應(yīng)的事件。請參閱捕獲回發(fā)事件。
AspNet技術(shù):ASP.NET Web Page應(yīng)用深入探討第1/2頁,轉(zhuǎn)載需保留來源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請第一時間聯(lián)系我們修改或刪除,多謝。