一、服務(wù)器腳本基礎介紹
首先,我們先復習一下Web服務(wù)器頁(yè)面的基本執行方式:
1、客戶(hù)端通過(guò)在瀏覽器的地址欄敲入地址來(lái)發(fā)送請求到服務(wù)器端
2、服務(wù)器接收到請求之后,發(fā)給相應的服務(wù)器端頁(yè)面(也就是腳本)來(lái)執行,腳本產(chǎn)生客戶(hù)端的響應,發(fā)送回客戶(hù)端
3、客戶(hù)端瀏覽器接收到服務(wù)器傳回的響應,對Html進(jìn)行解析,將圖形化的網(wǎng)頁(yè)呈現在用戶(hù)面前
對于服務(wù)器和客戶(hù)端的交互,通常通過(guò)下面幾種主要方式:
1、Form:這是最主要的方式,標準化的控件來(lái)獲取用戶(hù)的輸入,Form的提交將數據發(fā)送給服務(wù)器端處理
2、QueryString:通過(guò)在Url后面帶參數達到將參數傳送給服務(wù)器,這種方式其實(shí)跟Get方式的Form是一樣的
3、Cookies:這是一種比較特殊的方式,通常用于用戶(hù)身份的確認
二、ASP.Net簡(jiǎn)介
傳統的服務(wù)器腳本語(yǔ)言,如ASP、JSP等,編寫(xiě)服務(wù)器腳本的方式大同小異,都是在Html中嵌入解釋或編譯執行的代碼,由服務(wù)器平臺執行這些代碼來(lái)生成Html;對于這類(lèi)似的腳本,頁(yè)面的生存周期實(shí)際上很簡(jiǎn)單,就是從開(kāi)頭至末尾,執行完所有的代碼,當然用Java編寫(xiě)的Servlet可以編寫(xiě)更復雜的代碼,但是從結構上看,和JSP沒(méi)什么區別。
ASP.Net的出現,打破了這種傳統;ASP.Net采用了CodeBehind技術(shù)和服務(wù)器端控件,加入了服務(wù)器端的事件的概念,改變了腳本語(yǔ)言編寫(xiě)的模式,更加貼近Window編程,使Web編程更加簡(jiǎn)單、直觀(guān);但是我們要看到,ASP.Net本身并沒(méi)有改變Web編程的基本模式,只是封裝了一些細節、提供了一些易用的功能,使代碼更容易編寫(xiě)和維護;從某種程度上來(lái)說(shuō),將服務(wù)器端執行的方式復雜化了,這就是我們今天要討論的主體:ASP.Net Web Page的生存周期。
三、ASP.Net請求處理模式
我們說(shuō),ASP.Net的Web Page并沒(méi)有脫離Web編程的模式,所以它仍然是以 請求->接收請求->處理請求->發(fā)送響應 這樣的模式在工作,每一次與客戶(hù)端的交互都會(huì )引發(fā)一次新的請求,所以一個(gè)Web Page的生命周期是以一次請求為基礎的。
當IIS收到客戶(hù)端的請求的時(shí)候,會(huì )將請求交給aspnet_wp這個(gè)進(jìn)程來(lái)處理,這個(gè)進(jìn)程會(huì )查看請求的應用程序域是否存在,如果不存在則會(huì )創(chuàng )建一個(gè),然后會(huì )創(chuàng )建一個(gè)Http運行時(shí)(HttpRuntime)來(lái)處理請求,這個(gè)運行時(shí)“為當前應用程序提供一組 ASP.NET 運行時(shí)服務(wù)”(摘自MSDN)。
HttpRuntime在處理請求的時(shí)候,會(huì )維護一系列的應用程序實(shí)例,也就是應用程序的Global類(lèi)(global.asax)的實(shí)例,這些實(shí)例在沒(méi)有請求的時(shí)候,會(huì )存放在一個(gè)應用程序池中(實(shí)際上應用程序池由另一個(gè)類(lèi)來(lái)維護,HttpRuntime只是簡(jiǎn)單的調用),每接收到一個(gè)請求,HttpRuntime都會(huì )獲取一個(gè)閑置的實(shí)例來(lái)處理請求,這個(gè)實(shí)例在請求結束前不會(huì )處理其他的請求,處理完畢之后,它又會(huì )回到池中,“一個(gè)實(shí)例在其生存期內被用于處理多個(gè)請求,但它一次只能處理一個(gè)請求。”(摘自MSDN)
當應用程序實(shí)例處理請求的時(shí)候,它會(huì )創(chuàng )建請求頁(yè)面類(lèi)的實(shí)例,執行它的ProcessRequest方法來(lái)處理請求,這個(gè)方法也就是Web Page生命周期的開(kāi)始。
四、Aspx頁(yè)面與CodeBehind
在深入了解頁(yè)面的生命周期之前,我們先來(lái)探討一些Aspx與CodeBehind之間的關(guān)系。
<%@ Page language="c#" Codebehind="WebForm.aspx.cs" Inherits="MyNamespace.WebForm" %>
相信使用過(guò)CodeBehind技術(shù)的朋友,對ASPX頂部的這句話(huà)應該是非常熟悉了,我們來(lái)一項一項的分析它:
Page language="c#" 這個(gè)就不用多說(shuō)了吧
Codebehind="WebForm.aspx.cs" 這一句表示綁定的代碼文件
Inherits="MyNamespace.WebForm" 這句非常重要,它表示頁(yè)面繼承的類(lèi)名稱(chēng),也就是CodeBehind的代碼文件中的類(lèi),這個(gè)類(lèi)必須從System.Web.WebControls.Page派生
從上面我們可以分析出,實(shí)際上CodeBehind中的類(lèi)就是頁(yè)面(ASPX)的基類(lèi),到這里,可能有些朋友要問(wèn)了,在編寫(xiě)ASPX的時(shí)候,完全是按照ASP的方式,在Html中嵌入代碼或者嵌入服務(wù)器控件,沒(méi)有看到所謂“類(lèi)”的影子???
這個(gè)問(wèn)題實(shí)際上并不復雜,各位使用ASP.Net編程的朋友可以到你們的系統盤(pán):\WINDOWS\Microsoft.NET\Framework\<版本號>\Temporary ASP.NET Files這個(gè)目錄下,這個(gè)下面就放了所有本機上存在的ASP.Net應用程序的臨時(shí)文件,子目錄的名稱(chēng)就是應用程序的名稱(chēng),然后再下去兩層(為了保證唯一,ASP.Net自動(dòng)產(chǎn)生了兩層子目錄,并且子目錄名稱(chēng)是隨機的),然后我們會(huì )發(fā)現有很多類(lèi)似:“yfy1gjhc.dll”、“xeunj5u3.dll”這樣的鏈接庫以及“komee-bp.0.cs”、“9falckav.0.cs”這樣的源文件,實(shí)際上這就是ASPX被ASP.Net動(dòng)態(tài)編譯后的結果,打開(kāi)這些源文件我們可以發(fā)現:
public class WebForm_aspx : MyNamespace.WebForm, System.Web.SessionState.IRequiresSessionState
這就印證了我們前面的說(shuō)法,ASPX是代碼綁定類(lèi)的子類(lèi),它的名稱(chēng)是ASPX文件名加上“_aspx”后綴,通過(guò)研究這些代碼我們可以發(fā)現,實(shí)際上所有aspx中定義的服務(wù)器控件都是在這些代碼中生成的,然后動(dòng)態(tài)產(chǎn)生這些代碼的時(shí)候,把原來(lái)在A(yíng)SPX中嵌入的代碼寫(xiě)在了相應的位置。
當某個(gè)頁(yè)面第一次被訪(fǎng)問(wèn)的時(shí)候,Http運行時(shí)就會(huì )使用一個(gè)代碼生成器去解析ASPX文件并生成源代碼并編譯,然后以后的訪(fǎng)問(wèn)就直接調用編譯后的dll,這也是為什么ASPX第一次訪(fǎng)問(wèn)的時(shí)候非常慢的原因。
解釋了這個(gè)問(wèn)題,我們再來(lái)看另一個(gè)問(wèn)題。我們在使用代碼綁定的時(shí)候,在設計頁(yè)面拖一個(gè)控件,然后切換到代碼視圖,就可以直接在Page_Load中使用這個(gè)控件了,既然控件是在子類(lèi)中產(chǎn)生的,那為什么在父類(lèi)中可以直接使用呢?
實(shí)際上我們可以發(fā)現,每當用VS.Net拖一個(gè)控件到頁(yè)面上,代碼綁定文件中總是會(huì )類(lèi)似這樣的添加一個(gè)聲明:
protected System.Web.WebControls.Button Button1;
我們可以發(fā)現這個(gè)字段被聲明成protected,而且名字與ASPX中控件的ID一致,仔細想一想,這個(gè)問(wèn)題就迎刃而解了。我們前面提到ASPX的源代碼是被生成器動(dòng)態(tài)生成和編譯的,生成器會(huì )產(chǎn)生動(dòng)態(tài)生成每一個(gè)服務(wù)器控件的代碼,在生成的時(shí)候,它會(huì )檢查父類(lèi)有沒(méi)有聲明這個(gè)控件,如果聲明了,它會(huì )添加類(lèi)似下面的一句代碼:
this.DataGrid1 = __ctrl;
這個(gè)__ctrl就是生成該控件的變量,這時(shí)候它就把控件的引用賦給了父類(lèi)中相應的變量,這也是為什么父類(lèi)中的聲明必須為protected(實(shí)際上也可以為public),因為要保證子類(lèi)能夠調用。
然后在執行Page_Load的時(shí)候,因為這時(shí)候父類(lèi)的聲明已經(jīng)被子類(lèi)中的初始化代碼賦了值,所以我們就可以使用這個(gè)字段來(lái)訪(fǎng)問(wèn)對應的控件,了解了這些,我們就不會(huì )犯在代碼綁定文件中的構造器里使用控件,造成空引用的異常的錯誤了,因為構造器是最先執行的,這時(shí)候子類(lèi)的初始化還沒(méi)有開(kāi)始,所以父類(lèi)中的字段是空值,至于子類(lèi)是什么時(shí)候初始化我們放到后面討論。
五、頁(yè)面生存周期
現在回到第三個(gè)標題中講到的內容,我們講到了HttpApplication的實(shí)例接收請求,并創(chuàng )建頁(yè)面類(lèi)的實(shí)例,實(shí)際上這個(gè)實(shí)例也就是動(dòng)態(tài)編譯的ASPX的類(lèi)的一個(gè)實(shí)例,上一個(gè)標題中我們了解到ASPX實(shí)際上是代碼綁定中類(lèi)的子類(lèi),所以它繼承了所有的protected方法。
現在我們來(lái)看看VS.Net自動(dòng)生成的CodeBehind類(lèi)的代碼,以此來(lái)開(kāi)始我們對頁(yè)面生命周期的探討:
#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
//
// CODEGEN:該調用是 ASP.NET Web 窗體設計器所必需的。
//
InitializeComponent();
base.OnInit(e);
}
/// <summary>
/// 設計器支持所需的方法 - 不要使用代碼編輯器修改
/// 此方法的內容。
/// </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
這個(gè)就是使用VS.Net產(chǎn)生的Page的代碼,我們來(lái)看,這里面有兩個(gè)方法,一個(gè)是OnInit,一個(gè)是InitializeComponent,后者被前者調用,實(shí)際上這就是頁(yè)面初始化的開(kāi)始,在InitializeComponent中我們看到了控件的事件聲明和Page的Load聲明。
下面是從MSDN中摘錄的一段描述和一個(gè)頁(yè)面生命周期方法和事件觸發(fā)的順序表:
“每次請求 ASP.NET 頁(yè)時(shí),服務(wù)器就會(huì )加載一個(gè) ASP.NET 頁(yè),并在請求完成時(shí)卸載該頁(yè)。頁(yè)及其包含的服務(wù)器控件負責執行請求并將 HTML 呈現給客戶(hù)端。雖然客戶(hù)端和服務(wù)器之間的通訊是無(wú)狀態(tài)的和斷續的,但是必須使客戶(hù)感覺(jué)到這是一個(gè)連續執行的過(guò)程。”
“這種連續性假象是由 ASP.NET 頁(yè)框架、頁(yè)及其控件實(shí)現的?;匕l(fā)后,控件的行為必須看起來(lái)是從上次 Web 請求結束的地方開(kāi)始的。雖然 ASP.NET 頁(yè)框架可使執行狀態(tài)管理相對容易一些,但是為了獲得連續性效果,控件開(kāi)發(fā)人員必須知道控件的執行順序??丶_(kāi)發(fā)人員需要了解:在控件生命周期的各個(gè)階段,控件可使用哪些信息、保持哪些數據、控件呈現時(shí)處于哪種狀態(tài)。例如,在填充頁(yè)上的控件樹(shù)之前控件不能調用其父級。” “下表提供了控件生命周期中各階段的高級概述。有關(guān)詳細信息,請點(diǎn)擊表中的鏈接。”
階段 控件需要執行的操作 要重寫(xiě)的方法或事件
初始化 初始化在傳入 Web 請求生命周期內所需的設置。請參閱處理繼承的事件。 Init 事件(OnInit 方法)
加載視圖狀態(tài) 在此階段結束時(shí),就會(huì )自動(dòng)填充控件的 ViewState 屬性,詳見(jiàn)維護控件中的狀態(tài)中的介紹??丶梢灾貙?xiě) LoadViewState 方法的默認實(shí)現,以自定義狀態(tài)還原。 LoadViewState 方法
處理回發(fā)數據 處理傳入窗體數據,并相應地更新屬性。請參閱處理回發(fā)數據。
注意 只有處理回發(fā)數據的控件參與此階段。 LoadPostData 方法 (如果已實(shí)現IPostBackDataHandler)
加載 執行所有請求共有的操作,如設置數據庫查詢(xún)。此時(shí),樹(shù)中的服務(wù)器控件已創(chuàng )建并初始化、狀態(tài)已還原并且窗體控件反映了客戶(hù)端的數據。請參閱處理繼承的事件。 Load 事件
(OnLoad 方法)
發(fā)送回發(fā)更改通知 引發(fā)更改事件以響應當前和以前回發(fā)之間的狀態(tài)更改。請參閱處理回發(fā)數據。
注意 只有引發(fā)回發(fā)更改事件的控件參與此階段。 RaisePostDataChangedEvent 方法
(如果已實(shí)現 IPostBackDataHandler)
處理回發(fā)事件 處理引起回發(fā)的客戶(hù)端事件,并在服務(wù)器上引發(fā)相應的事件。請參閱捕獲回發(fā)事件。
注意 只有處理回發(fā)事件的控件參與此階段。 RaisePostBackEvent 方法
(如果已實(shí)現 IPostBackEventHandler)
預呈現 在呈現輸出之前執行任何更新??梢员4嬖陬A呈現階段對控件狀態(tài)所做的更改,而在呈現階段所對的更改則會(huì )丟失。請參閱處理繼承的事件。 PreRender 事件
(OnPreRender 方法)
保存狀態(tài) 在此階段后,自動(dòng)將控件的 ViewState 屬性保持到字符串對象中。此字符串對象被發(fā)送到客戶(hù)端并作為隱藏變量發(fā)送回來(lái)。為了提高效率,控件可以重寫(xiě) SaveViewState 方法以修改 ViewState 屬性。請參閱維護控件中的狀態(tài)。 SaveViewState 方法
呈現 生成呈現給客戶(hù)端的輸出。請參閱呈現 ASP.NET 服務(wù)器控件。 Render 方法
處置 執行銷(xiāo)毀控件前的所有最終清理操作。在此階段必須釋放對昂貴資源的引用,如數據庫鏈接。請參閱 ASP.NET 服務(wù)器控件中的方法。
Dispose 方法
卸載 執行銷(xiāo)毀控件前的所有最終清理操作??丶髡咄ǔT?Dispose 中執行清除,而不處理此事件。 UnLoad 事件(On UnLoad 方法)
從這個(gè)表里面我們可以清楚的看到一個(gè)Page從裝載到卸載之間調用的方法和觸發(fā)的時(shí)間,接下來(lái)我們就深入的對其進(jìn)行一些分析。
看了上面的表,細心的朋友可能要問(wèn)了,既然OnInit是頁(yè)面生命周期的開(kāi)始,而我們在上一講中談到控件在子類(lèi)中被創(chuàng )建,那么在這里實(shí)際上在InitializeComponent方法中我們已經(jīng)可以使用父類(lèi)中聲名的字段了,那么就意味著(zhù)子類(lèi)的初始化更在這之前?
在第三個(gè)標題中我們講到了頁(yè)面類(lèi)的ProcessRequest才是真正意義上的頁(yè)面聲明周期的開(kāi)始,這個(gè)方法是由HttpApplication調用的(其中調用的方式比較復雜,有機會(huì )單獨撰文來(lái)講解),一個(gè)Page對請求的處理就是從這個(gè)方法開(kāi)始,通過(guò)反編譯.Net類(lèi)庫來(lái)查看源代碼,我們發(fā)現在System.Web.WebControls.Page的基類(lèi):System.Web.WebControls.TemplateControl(它是頁(yè)面和用戶(hù)控件的基類(lèi))中定義了一個(gè)“FrameworkInitialize”虛擬方法,然后在Page的ProcessRequest中最先調用了這個(gè)方法,在生成器生成的ASPX的源代碼中我們發(fā)現了這個(gè)方法的蹤影,所有的控件都在這個(gè)方法中被初始化,頁(yè)面的控件樹(shù)就在這個(gè)時(shí)候產(chǎn)生。
接下來(lái)的事情就簡(jiǎn)單了,我們來(lái)逐步分析頁(yè)面生命周期的每一項:
1、初始化
初始化對應Page的Init事件和OnInit方法。
如果要重寫(xiě),MSDN推薦的方式是重載OnInti方法,而不是增加一個(gè)Init事件的代理,這兩者是有差別的,前者可以控制調用父類(lèi)OnInit方法的順序,而后者只能在父類(lèi)的OnInit后執行(實(shí)際上是在OnInit里面被調用的)。
2、 加載視圖狀態(tài)
這是個(gè)比較重要的方法,我們知道,對于每次請求,實(shí)際上是由不同的頁(yè)面類(lèi)實(shí)例來(lái)處理的,為了保證兩次請求間的狀態(tài),ASP.Net使用了ViewState。
LoadViewState方法就是從ViewState中獲取上一次的狀態(tài),并依照頁(yè)面的控件樹(shù)的結構,用遞歸來(lái)遍歷整個(gè)樹(shù),將對應的狀態(tài)恢復到每一個(gè)控件上。
3、 處理回發(fā)數據
這個(gè)方法是用來(lái)檢查客戶(hù)端發(fā)回的控件數據的狀態(tài)是否發(fā)生了改變。方法的原型:
public virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection)
postDataKey是標識控件的關(guān)鍵字(也就是postCollection中的Key),postCollection是包含回發(fā)數據的集合,我們可以重寫(xiě)這個(gè)方法,然后檢查回發(fā)的數據是否發(fā)生了變化,如果是則返回一個(gè)True,“如果控件狀態(tài)因回發(fā)而更改,則 LoadPostData 返回 true;否則返回 false。頁(yè)框架跟蹤所有返回 true 的控件并在這些控件上調用 RaisePostDataChangedEvent。”(摘自MSDN)
這個(gè)方法是System.Web.WebControls.Control中定義的,也是所有需要處理事件的自定義控件需要處理的方法,對于我們今天討論的Page來(lái)說(shuō),可以不用管它。
4、 加載
加載對應Load事件和OnLoad方法,對于這個(gè)事件,相信大多數朋友都會(huì )比較熟悉,用VS.Net生成的頁(yè)面中的Page_Load方法就是響應Load事件的方法,對于每一次請求,Load事件都會(huì )觸發(fā),Page_Load方法也就會(huì )執行,相信這也是大多數人了解ASP.Net的第一步。
Page_Load方法響應了Load事件,這個(gè)事件是在System.Web.WebControl.Control類(lèi)中定義的(這個(gè)類(lèi)是Page和所有服務(wù)器控件的祖宗),并且在OnLoad方法中被觸發(fā)。
很多人可能碰到過(guò)這樣的事情,寫(xiě)了一個(gè)PageBase類(lèi),然后在Page_Load中來(lái)驗證用戶(hù)信息,結果發(fā)現不管驗證是否成功,子類(lèi)頁(yè)面的Page_Load總是會(huì )先執行,這個(gè)時(shí)候很可能留下一些安全性的隱患,用戶(hù)可能在沒(méi)有得到驗證的情況下就執行了子類(lèi)中的Page_Load方法。
出現這個(gè)問(wèn)題的原因很簡(jiǎn)單,因為Page_Load方法是在OnInit中被添加到Load事件中的,而子類(lèi)的OnInit方法中是先添加了Load事件,然后再調用base.OnInit,這樣就造成了子類(lèi)的Page_Load被先添加,那么先執行了。
要解決這個(gè)問(wèn)題也很簡(jiǎn)單,有兩種方法:
1) 在PageBase中重載OnLoad方法,然后在OnLoad中驗證用戶(hù),然后調用base.OnLoad,因為L(cháng)oad事件是在OnLoad中觸發(fā),這樣我們就可以保證在觸發(fā)Load事件之前驗證用戶(hù)。
2) 在子類(lèi)的OnInit方法中先調用base.OnInit,這樣來(lái)保證父類(lèi)先執行Page_Load
5、 發(fā)送回發(fā)更改通知
這個(gè)方法對應第3步的處理回發(fā)數據,如果處理回發(fā)數據返回True,頁(yè)面框架就會(huì )調用此方法來(lái)觸發(fā)數據更改的事件,所以自定義控件的回發(fā)數據更改事件需要在此方法中觸發(fā)。
同樣這個(gè)方法對于Page來(lái)說(shuō),沒(méi)有太大的用處,當然你也可以在Page的基礎上自己定義數據更改的事件,這當然也是可以的。
6、 處理回發(fā)事件
這個(gè)方法是大多數服務(wù)器控件事件引發(fā)的地方,當請求中包含控件事件觸發(fā)的信息時(shí)(服務(wù)器控件的事件是另一個(gè)論題,我會(huì )在不久將來(lái)另外撰文討論),頁(yè)面控件會(huì )調用相應控件的RaisePostBackEvent方法來(lái)引發(fā)服務(wù)器端的事件。
這里又引出一個(gè)常見(jiàn)的問(wèn)題:
經(jīng)常有網(wǎng)友問(wèn),為什么修改提交后的數據并沒(méi)有更改
多數的情況都是他們沒(méi)有理解服務(wù)器事件的觸發(fā)流程,我們可以看出,觸發(fā)服務(wù)器事件是在Page的Load之后,也就是說(shuō)頁(yè)面會(huì )先執行Page_Load,然后才會(huì )執行按鈕(這里以按鈕為例)的點(diǎn)擊事件,很多朋友都是在Page_Load中綁定數據,然后在按鈕事件中處理更改,這樣做有一個(gè)毛病,Page_Load永遠都是在按鈕事件之前執行,那么意味著(zhù)數據還沒(méi)來(lái)得及更改,Page_Load中的數據綁定的代碼就先執行了,原有的數據又賦給了控件,那么執行按鈕事件的時(shí)候,實(shí)際上獲得的是原有的數據,那么更新當然就沒(méi)有效果了。
更改這個(gè)問(wèn)題也非常簡(jiǎn)單,比較合理的做法是把數據綁定的代碼寫(xiě)成一個(gè)方法,我們假設為BindData:
private void BindData()
{
//綁定數據
}
然后修改PageLoad:
private void Page_Load( object sender,EventArgs e )
{
if( !IsPostBack )
{
BindData(); //在頁(yè)面第一次訪(fǎng)問(wèn)的時(shí)候綁定數據
}
}
最后在按鈕事件中:
private Button1_Click( object sender,EventArgs e )
{
//更新數據
BindData();//重新綁定數據
}
7、預呈現
最終請求的處理都會(huì )轉變?yōu)榘l(fā)回服務(wù)器的響應,預呈現這個(gè)階段就是執行在最終呈現之前所作的狀態(tài)的更改,因為在呈現一個(gè)控件之前,我們必須根據它的屬性來(lái)產(chǎn)生Html,比如Style屬性,這是最典型的例子,在預呈現之前,我們可以更改一個(gè)控件的Style,當執行預呈現的時(shí)候,我們就可以把Style保存下來(lái),作為呈現階段顯示Html的樣式信息。
8、保存狀態(tài)
這個(gè)階段是針對加載狀態(tài)的,我們多次提到,請求之間是不同的實(shí)例在處理,所以我們需要把本次的頁(yè)面和控件的狀態(tài)保存起來(lái),這個(gè)階段就是把狀態(tài)寫(xiě)入ViewState的階段。
9、呈現
到這里,實(shí)際上頁(yè)面對請求的處理基本就告一段落了,在Render方法中,會(huì )遞歸整個(gè)頁(yè)面的控件樹(shù),依次調用Render方法,把對應的Html代碼寫(xiě)入最終響應的流中。
10、處置
實(shí)際上就是Dispose方法,在這個(gè)階段會(huì )釋放占用的資源,例如數據庫連接。
11、卸載
最后,頁(yè)面會(huì )執行OnUnLoad方法觸發(fā)UnLoad事件,處理在頁(yè)面對象被銷(xiāo)毀之前的最后處理,實(shí)際上ASP.Net提供這個(gè)事件只是設計上的考慮,通常資源的釋放都會(huì )在Dispose方法中完成,所以這個(gè)方法也變成雞肋了。
我們簡(jiǎn)單的介紹了頁(yè)面的生存周期,對于服務(wù)器端事件的處理做了不太深入的講解,今天主要是想大家了解頁(yè)面執行的周期,對于服務(wù)器控件的事件和生存期我會(huì )在后續在寫(xiě)一些文章來(lái)探討。
這些內容是我在學(xué)習ASP.Net的時(shí)候對Page研究的一些心得,具體的細節沒(méi)有很詳細的探討,更多的內容請大家參考MSDN,但是我舉了一些初學(xué)者常犯的錯誤和出現錯誤的原因,希望可以給大家帶來(lái)啟發(fā)。