開(kāi)篇:上一篇我們了解了所謂的請(qǐng)求處理管道,在眾多的事件中微軟開(kāi)放了19個(gè)重要的事件給我們,我們可以注入一些自定義的業(yè)務(wù)邏輯實(shí)現(xiàn)應(yīng)用的個(gè)性化設(shè)計(jì)。本篇,我們來(lái)看看WebForm模式下的頁(yè)面生命周期。
一、ASP.Net Page的兩個(gè)重要部分在前面對(duì)于請(qǐng)求處理管道的介紹中,我們已經(jīng)了解了一個(gè)ASP.NET WebForm頁(yè)面請(qǐng)求事件的整體流程。那么,在其中一個(gè)最重要的部分就是ASP.NET Page頁(yè)面,但是我們并沒(méi)有對(duì)其進(jìn)行詳細(xì)討論。因此,我們?cè)诖松钊氲亓私庖幌翧SP.NET頁(yè)面事件。 每一個(gè)ASP.NET Page頁(yè)都有2個(gè)部分:一個(gè)部分是在瀏覽器中進(jìn)行顯示的部分,它包含了HTML標(biāo)簽、viewstate形式的隱藏域 以及 在HTML input中的數(shù)據(jù)。當(dāng)這個(gè)頁(yè)面被提交到服務(wù)器時(shí),這些HTML標(biāo)簽會(huì)被創(chuàng)建到ASP.NET控件,并且viewstate還會(huì)和表單數(shù)據(jù)綁定在一起。另一個(gè)部分是在xxx.cs文件中的進(jìn)行業(yè)務(wù)邏輯操作的部分,一旦你在后置代碼中得到所有的服務(wù)器控件,你可以執(zhí)行和寫(xiě)入你自己的邏輯并呈現(xiàn)給客戶(hù)瀏覽器。 其中,后臺(tái)代碼類(lèi)是前臺(tái)頁(yè)面類(lèi)的父類(lèi),前臺(tái)頁(yè)面類(lèi)則是后臺(tái)代碼類(lèi)的子類(lèi)。這一點(diǎn),可以通過(guò)查看每個(gè)aspx文件中的頭部,我們都會(huì)看到以下的一句代碼: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="FirstPage.aspx.cs" Inherits="WebFormDemo.FirstPage" %> 其中CodeBehind這個(gè)屬性定義了此aspx頁(yè)面的專(zhuān)屬后臺(tái)代碼文件的名稱(chēng),而Inherits這個(gè)屬性則定義了此aspx頁(yè)面所要繼承的父類(lèi)的名稱(chēng)(這也可以簡(jiǎn)單地說(shuō)明,aspx頁(yè)面會(huì)單獨(dú)生成一個(gè)類(lèi),與后臺(tái)代碼類(lèi)不重合在一起)。因此,aspx.cs就是aspx的后置處理代碼,負(fù)責(zé)處理aspx中<%%>和runat="server"的內(nèi)容。 現(xiàn)在這些HTML控件會(huì)作為ASP.NET控件存活在服務(wù)器上,ASP.NET會(huì)觸發(fā)一系列的事件,我們也可以在這些事件中注入自定義邏輯代碼。根據(jù)你想要執(zhí)行什么樣的任務(wù)/邏輯,我們需要將邏輯合理地放入這些事件之中。
二、ASP.Net Page的頁(yè)面事件流程
三、反編譯探秘ASP.Net Page頁(yè)面生命周期前面我們簡(jiǎn)單地了解了一下ASP.NET Page的頁(yè)面事件,現(xiàn)在我們來(lái)通過(guò)Reflector反編譯一下一個(gè)demo程序集,來(lái)感受一下ASP.NET Page的頁(yè)面生命周期。 3.1 準(zhǔn)備一個(gè)ASP.NET項(xiàng)目?。?)假如我們有以下的名為Index的一個(gè)aspx頁(yè)面: ![]() ?。?)Index所對(duì)應(yīng)的后臺(tái)代碼如下: ![]() 這里,我們來(lái)重點(diǎn)關(guān)注一下這個(gè)方法:我們可以通過(guò)寫(xiě)入以下代碼,然后在aspx中<% GetDllInfo(); %>調(diào)用,它顯示了我們這個(gè)ASP.NET項(xiàng)目所屬的程序集在哪個(gè)位置? protected void GetDllInfo() { Response.Write("頁(yè)面類(lèi)名稱(chēng):"+this.GetType() + "<br/>"); Response.Write("程序集地址:"+this.GetType().Assembly.Location + "<br/>"); Response.Write("父類(lèi)的名稱(chēng):"+this.GetType().BaseType + "<br/>"); Response.Write("程序集地址:"+this.GetType().BaseType.Assembly.Location + "<br/>"); } 瀏覽頁(yè)面,會(huì)顯示以下結(jié)果:通過(guò)下圖可以看到,我們的Index這個(gè)頁(yè)面會(huì)生成一個(gè)ASP.index_aspx的類(lèi),其父類(lèi)是Index。 3.2 反編譯生成的臨時(shí)程序集?、賹LL拖到Reflector中進(jìn)行查看源代碼通過(guò)上面顯示的路徑找到dll,并拖到反編譯工具(ILSpy或者Reflector,前者開(kāi)源免費(fèi),后者已經(jīng)收費(fèi),但天朝,你懂的。)進(jìn)行查看。通過(guò)下圖可以看出,頁(yè)面類(lèi)aspx是后臺(tái)代碼類(lèi)所綁定的子類(lèi),它的名稱(chēng)是aspx文件名加上“_aspx”后綴。因此,這里也就解釋了為什么在aspx中要訪問(wèn)的方法必須是public和protected的訪問(wèn)修飾符才可以。 ?、谝粋€(gè)大型Control:Page類(lèi)從上面可以看出,頁(yè)面類(lèi)繼承自后置代碼類(lèi),而后置代碼類(lèi)又繼承自Page類(lèi)。我們從上一篇管道可以知道,在請(qǐng)求處理管道的第8個(gè)事件中創(chuàng)建了Page類(lèi)對(duì)象,那么我們?nèi)タ纯碢age類(lèi)。 Page類(lèi)繼承自TemplateControl,顧名思義,Page類(lèi)是否就是一個(gè)模板控件呢?再看看TemplateControl類(lèi): 果不其然,其父類(lèi)是Control類(lèi),Page就是一個(gè)封裝過(guò)的大控件!那么,我們?cè)赑age中拖的那些runat="server"的服務(wù)器控件,又是保存在哪里的呢? 原來(lái),在Control父類(lèi)中,有一個(gè)Controls的屬性,它是一個(gè)控件的集合:Page中的所有控件,都會(huì)存在于這個(gè)集合中。 ?、垌?yè)面生命周期的入口:Page類(lèi)的ProcessRequest方法從上一篇請(qǐng)求處理管道中,我們知道在第11和第12個(gè)事件之間會(huì)調(diào)用Page類(lèi)對(duì)象的ProcessRequest方法進(jìn)入頁(yè)面生命周期。那么我們來(lái)看看這個(gè)ProcessRequest方法: 從圖中可以看出,這個(gè)方法中首先通過(guò)調(diào)用頁(yè)面類(lèi)對(duì)象(我們請(qǐng)求的頁(yè)面都是繼承于Page類(lèi)的)重寫(xiě)的FrameworkInitialize方法開(kāi)始我們經(jīng)常聽(tīng)到的構(gòu)造控件樹(shù)的過(guò)程。下面我們轉(zhuǎn)到index_aspx這個(gè)頁(yè)面類(lèi)重寫(xiě)的FrameworkInitialize方法中取看看是否是進(jìn)行了構(gòu)造頁(yè)面控件樹(shù)的操作:
?、蹷uildControlTree:構(gòu)造頁(yè)面控件樹(shù)看到這里,我們不由地想問(wèn),什么是頁(yè)面控件樹(shù)?在一個(gè)aspx頁(yè)面中,runat="server"的控件集合構(gòu)成了如下圖所示的一棵頁(yè)面控件樹(shù),他們被一一實(shí)例化,并依據(jù)層級(jí)關(guān)系存儲(chǔ)到了controls集合中。 了解了什么是頁(yè)面控件樹(shù),現(xiàn)在我們看看是如何來(lái)構(gòu)造這棵樹(shù)的,通過(guò)查看BuildControlTree方法,發(fā)現(xiàn)它調(diào)用了多個(gè)名為BuildControlX的方法,依次實(shí)例化我們頁(yè)面中所需的控件,并添加到控件集合中(這里其實(shí)是將這些服務(wù)器控件作為子控件添加到頁(yè)面(頁(yè)面本身就是一個(gè)大的控件)中,在樹(shù)形結(jié)構(gòu)中Page就是一個(gè)根節(jié)點(diǎn),而那些Page中的控件則是Page的孩子節(jié)點(diǎn))。 那么,這些BuildControlX(X代表數(shù)字)方法又在做些什么事呢?我們可以通過(guò)查看一個(gè)BuildControl方法,看看如何打造HtmlForm的: 可以看出,在構(gòu)造HtmlForm控件的過(guò)程中,不僅為其設(shè)置了ID(_ctrl.ID="formIndex"),還為其指定了渲染方法(通過(guò)設(shè)置委托_ctrl.SetRenderMethodDelegate())。又因?yàn)槲覀兺狭艘粋€(gè)TextBox和Button在其中,于是在實(shí)例化HtmlForm這個(gè)控件的途中,又去實(shí)例化TextBox和Button對(duì)象,并將其作為HtmlForm的子節(jié)點(diǎn),形成一個(gè)層級(jí)關(guān)系。 ?、荽_定IsPostBack:是否第一次請(qǐng)求該頁(yè)面現(xiàn)在重新回到Page類(lèi)的ProcessRequest方法中,在創(chuàng)建頁(yè)面控件樹(shù)完成之后,開(kāi)始進(jìn)入一個(gè)ProcessRequestMain方法,這個(gè)方法則真正地開(kāi)啟了頁(yè)面生命周期之門(mén)。 private void ProcessRequest(bool includeStagesBeforeAsyncPoint, bool includeStagesAfterAsyncPoint) { ...... this.ProcessRequestMain(includeStagesBeforeAsyncPoint, includeStagesAfterAsyncPoint); ...... } 我們經(jīng)常在Page_Load方法中使用Page.IsPostBack屬性來(lái)判斷請(qǐng)求是否是回發(fā),那么它是在哪里設(shè)置的呢?原來(lái),在ProcessRequestMain方法中: ?、蕹跏蓟僮鳎篜reInit-->Init-->InitComplete接下來(lái)就是初始化操作了,初始化操作分為了三個(gè)階段:預(yù)初始化、初始化(使用遞歸方式)、初始化完成。 private void ProcessRequestMain(bool includeStagesBeforeAsyncPoint, bool includeStagesAfterAsyncPoint) { ...... this.PerformPreInit(); ...... this.InitRecursive(); ...... this.OnInitComplete(); ...... } 預(yù)初始化主要利用App_Themes目錄中的內(nèi)容進(jìn)行初始化主題,并應(yīng)用模板頁(yè)。 這里我們主要看看初始化操作,通過(guò)查看源代碼,可以看出,該方法通過(guò)遞歸調(diào)用子控件的初始化方法,完成了控件集合中所有控件的初始化操作。 internal virtual void InitRecursive(Control namingContainer) { ...... int count = this._controls.Count; for (int i = 0; i < count; i++) { Control control = this._controls[i]; control.UpdateNamingContainer(namingContainer); if (((control._id == null) && (namingContainer != null)) && !control.flags[0x40]) { control.GenerateAutomaticID(); } control._page = this.Page; control.InitRecursive(namingContainer); } ...... } 再看看初始化方法中都做了哪些初始化操作,細(xì)細(xì)一看,原來(lái)就是為其動(dòng)態(tài)地生成一個(gè)ID(control.GenerateAutomaticID()),然后將該控件的page指針指向當(dāng)前Page頁(yè)等。PreLoad 預(yù)加載在 Load 事件之前對(duì)頁(yè)或控件執(zhí)行處理, ⑦加載操作:(LoadState-->ProcessPostData-->)PreLoad-->Load-->(ProcessPostData-->RaiseChangedEvents-->RaisePostBackEvent-->)LoadComplete
初始化完成之后,ASP.NET會(huì)通過(guò)IsPostBack判斷是否是第一次請(qǐng)求,如果不是,那么首先會(huì)加載ViewState并對(duì)回發(fā)的數(shù)據(jù)進(jìn)行處理。 private void ProcessRequestMain(bool includeStagesBeforeAsyncPoint, bool includeStagesAfterAsyncPoint) { if(this.IsPostBack) { ...... this.LoadAllState(); ...... this.ProcessPostData(this._requestValueCollection, true); ...... } } 至于ViewState是什么?又不了解的朋友,可以瀏覽我的另一篇博文:ASP.NET WebForm溫故知新:ViewState,這里就不再贅述。這里LoadAllState方法主要是將隱藏域中的_VIEWSTATE通過(guò)解碼獲取控件的狀態(tài)與數(shù)據(jù)信息,而ProcessPostData方法則是進(jìn)行了兩個(gè)部分的操作:一是將剛剛獲取到的各個(gè)控件的狀態(tài)與數(shù)據(jù)信息填充到頁(yè)面控件樹(shù)中所對(duì)應(yīng)的各個(gè)控件中去,二是對(duì)比控件狀態(tài)是否發(fā)生了改變?比如被點(diǎn)擊了?被觸發(fā)了某個(gè)事件(例如TextChanged、SelectedIndexChanged等)?如有觸發(fā)事件,則把需要觸發(fā)事件的控件放到一個(gè)集合當(dāng)中去。
處理完ViewState后,就開(kāi)始進(jìn)行正式地加載操作了,如下代碼所示: private void ProcessRequestMain(bool includeStagesBeforeAsyncPoint, bool includeStagesAfterAsyncPoint) { ...... this.OnPreLoad(EventArgs.Empty); ...... this.LoadRecursive(); ...... } 在正式加載過(guò)程中也分為了兩個(gè)部分,一個(gè)是PreLoad預(yù)加載,另外一個(gè)則是重頭戲Load加載(通過(guò)方法名可以推斷,該方法是通過(guò)遞歸方式調(diào)用加載的)。首先,調(diào)用了OnPreLoad方法進(jìn)行預(yù)加載操作,如果我們需要在 Load 事件之前對(duì)頁(yè)或控件(這時(shí)頁(yè)面控件樹(shù)已經(jīng)構(gòu)造完成)執(zhí)行處理,就可以使用該事件。通過(guò)查看源代碼,在PreLoad方法中會(huì)遍歷一個(gè)PreLoad事件集合(我們可以自定義注入我們想要的事件),然后依次執(zhí)行委托所持有的事件。 protected virtual void OnPreLoad(EventArgs e) { EventHandler handler = (EventHandler) base.Events[EventPreLoad]; if (handler != null) { handler(this, e); } } PreLoad之后就是重頭戲,也是我們最為熟悉的Load了,在調(diào)用LoadRecursive()方法進(jìn)入Load事件。 internal virtual void LoadRecursive() { if (this._controlState < ControlState.Loaded) { if (this.AdapterInternal != null) { this.AdapterInternal.OnLoad(EventArgs.Empty); } else { this.OnLoad(EventArgs.Empty); } } if (this._controls != null) { string errorMsg = this._controls.SetCollectionReadOnly("Parent_collections_readonly"); int count = this._controls.Count; for (int i = 0; i < count; i++) { this._controls[i].LoadRecursive(); } this._controls.SetCollectionReadOnly(errorMsg); } if (this._controlState < ControlState.Loaded) { this._controlState = ControlState.Loaded; } } 從上面可以看出:ASP.NET頁(yè)面首先調(diào)用自身的OnLoad方法以引發(fā)自身的Load事件,接著遞歸調(diào)用 Contorls 集合中各個(gè)控件的OnLoad方法以引發(fā)它們的Load事件。那么,我們?cè)陧?yè)面后置代碼類(lèi)中經(jīng)常使用的Page_Load事件方法是在哪里調(diào)用的呢?相信我們都有了答案,就在頁(yè)面自身的OnLoad方法中。
private void ProcessRequestMain(bool includeStagesBeforeAsyncPoint, bool includeStagesAfterAsyncPoint) { if(this.IsPostBack) { ...... this.ProcessPostData(this._leftoverPostData, false); ...... this.RaiseChangedEvents(); ...... this.RaisePostBackEvent(this._requestValueCollection); ...... } } 加載結(jié)束后,會(huì)經(jīng)歷第二次的處理回發(fā)數(shù)據(jù)的事件。那么,我們不禁會(huì)問(wèn),為何還要第二次進(jìn)行ProcessPostData方法的調(diào)用,我們剛剛不是都已經(jīng)對(duì)ViewState進(jìn)行了解碼并對(duì)應(yīng)到了對(duì)應(yīng)控件樹(shù)中的控件了嘛?這里,我們首先看看下面一段代碼: protected void Page_Load(object sender, EventArgs e) { if (IsPostBack) { TextBox txtTest = new TextBox(); txtTest.Text = "動(dòng)態(tài)創(chuàng)建的TextBox"; formIndex.Controls.Add(txtTest); } } 假如我們要在Page_Load事件中動(dòng)態(tài)地為Form添加一個(gè)TextBox控件,那么之前的頁(yè)面控件樹(shù)就發(fā)生了改變,所以,這里需要進(jìn)行第二次的ProcessPostData方法,現(xiàn)在豁然開(kāi)朗了吧。
在第二次處理回發(fā)數(shù)據(jù)之后,會(huì)調(diào)用RaiseChangedEvents方法觸發(fā)控件狀態(tài)改變事件響應(yīng)方法,例如TextBox_TextChanged、DropDownList_SelectedIndexChanged事件(這些事件中不包括Button_Click這種回發(fā)事件)等。查看源代碼,通過(guò)遍歷狀態(tài)改變了的控件的集合(在第一次進(jìn)行ProcessPostData時(shí)會(huì)檢查控件的狀態(tài)是否發(fā)生了改變,如果改變了就添加到一個(gè)集合中) internal void RaiseChangedEvents() { if (this._changedPostDataConsumers != null) { for (int i = 0; i < this._changedPostDataConsumers.Count; i++) { Control control = (Control) this._changedPostDataConsumers[i]; if (control != null) { IPostBackDataHandler postBackDataHandler = control.PostBackDataHandler; if (((control == null) || control.IsDescendentOf(this)) && ((control != null) && (control.PostBackDataHandler != null))) { postBackDataHandler.RaisePostDataChangedEvent(); } } } } } 在處理完?duì)顟B(tài)改變事件響應(yīng)方法后,會(huì)調(diào)用RaisePostBackEvent方法觸發(fā)例如按鈕控件的回發(fā)事件,例如Button_Click回發(fā)事件。 private void RaisePostBackEvent(NameValueCollection postData) { if (this._registeredControlThatRequireRaiseEvent != null) { this.RaisePostBackEvent(this._registeredControlThatRequireRaiseEvent, null); } else { string str = postData["__EVENTTARGET"]; bool flag = !string.IsNullOrEmpty(str); if (flag || (this.AutoPostBackControl != null)) { Control control = null; if (flag) { control = this.FindControl(str); } if ((control != null) && (control.PostBackEventHandler != null)) { string eventArgument = postData["__EVENTARGUMENT"]; this.RaisePostBackEvent(control.PostBackEventHandler, eventArgument); } } else { this.Validate(); } } } 通過(guò)查看代碼,發(fā)現(xiàn)通過(guò)回傳的表單數(shù)據(jù)中根據(jù)__EVENTTARGET與__EVENTARGUMENT進(jìn)行事件的觸發(fā)。我們可以通過(guò)查看ASP.NET生成的前端HTML代碼看到這兩個(gè)參數(shù):下圖是一個(gè)設(shè)置為AutoPostBack的DropDownList控件,可以發(fā)現(xiàn)回發(fā)事件都是通過(guò)調(diào)用_doPostBack這個(gè)js代碼進(jìn)行表單的submit,而表單中最重要的兩個(gè)參數(shù)就是eventTarget和eventArgument。 通過(guò)瀏覽器提供的開(kāi)發(fā)人員工具查看數(shù)據(jù)請(qǐng)求報(bào)文,可以看到除了提交form中的input外,還提交了ASP.Net WebForm預(yù)置的一些隱藏字段,而這些隱藏字段則是WebForm為我們提供便利的基礎(chǔ)。比如EventTarget則記錄剛剛提交給服務(wù)器的是哪個(gè)服務(wù)器控件。 事件觸發(fā)完成之后,加載操作就完成了,這時(shí)會(huì)調(diào)用OnLoadComplete方法進(jìn)行相關(guān)的事件,這里就不再贅述了。
這一階段就進(jìn)入了頁(yè)面生命周期的尾巴,開(kāi)始最終頁(yè)面的渲染流程: private void ProcessRequestMain(bool includeStagesBeforeAsyncPoint, bool includeStagesAfterAsyncPoint) { ...... this.PreRenderRecursiveInternal(); ...... this.PerformPreRenderComplete(); ...... this.SaveAllState(); ...... this.OnSaveStateComplete(EventArgs.Empty); ...... this.RenderControl(this.CreateHtmlTextWriter(this.Response.Output)); ...... } 這里我們主要看看PreRender、SaveState和Render三個(gè)事件。 既然已經(jīng)進(jìn)入了頁(yè)面渲染階段,為何還要有一個(gè)PreRender預(yù)呈現(xiàn)階段?通過(guò)查找資料,我們發(fā)現(xiàn)微軟這么設(shè)計(jì)是為了給開(kāi)發(fā)者提供一個(gè)最后一次更改頁(yè)面控件狀態(tài)或數(shù)據(jù)的機(jī)會(huì),也就說(shuō):你可以再在這里注入一個(gè)邏輯,最后一次改變控件值,或者統(tǒng)一地改變控件狀態(tài)為某個(gè)指定狀態(tài)。 然后就是SaveState,這個(gè)很好理解,也就說(shuō):剛剛給了你最后一次更改的機(jī)會(huì)結(jié)束后,我就要保存最終的ViewState了。這里需要注意的是:服務(wù)器在向?yàn)g覽器返回html之前,對(duì)ViewState中的內(nèi)容是進(jìn)行了Base64編碼的; 最后就是Render,進(jìn)行最終的頁(yè)面呈現(xiàn)了,換句話說(shuō):就是拼接形成HTML字符串。在這個(gè)階段,Page 對(duì)象會(huì)遍歷頁(yè)面控件樹(shù)并在每個(gè)控件上遞歸地調(diào)用此方法。所有 ASP.NET Web 服務(wù)器控件都有一個(gè)用于寫(xiě)出發(fā)送給瀏覽器的控件標(biāo)記的 Render 方法。通過(guò)對(duì)源代碼進(jìn)行追蹤,可以看到以下代碼: internal void RenderChildrenInternal(HtmlTextWriter writer, ICollection children) { if ((this.RareFields != null) && (this.RareFields.RenderMethod != null)) { writer.BeginRender(); this.RareFields.RenderMethod(writer, this); writer.EndRender(); } else if (children != null) { foreach (Control control in children) { control.RenderControl(writer); } } } 在Render過(guò)程中,會(huì)判斷當(dāng)前控件是否含有子控件集合,如果有,那么遍歷各個(gè)子控件的Render方法進(jìn)行HTML的渲染。可以想象,從頁(yè)面控件樹(shù)的根節(jié)點(diǎn)調(diào)用Render方法,會(huì)依次遞歸調(diào)用其所有子節(jié)點(diǎn)的Render方法,從而得到一個(gè)完整的HTML代碼。 那么,Render方法結(jié)束后,生成的HTML代碼保存到了哪里呢?原來(lái),Render方法的輸出會(huì)寫(xiě)入Page類(lèi)對(duì)象的 Response 屬性的 OutputStream 中,這就是最終的輸出流作為響應(yīng)報(bào)文通過(guò)HTTP協(xié)議返回給瀏覽器端了。
自此,狹義上的頁(yè)面生命周期就結(jié)束了,但廣義上的頁(yè)面聲明周期事件還未結(jié)束,還會(huì)經(jīng)歷一個(gè)UnLoad事件,該事件首先針對(duì)每個(gè)控件發(fā)生,繼而針對(duì)該頁(yè)發(fā)生。在控件中,使用該事件對(duì)特定控件執(zhí)行最后清理,如關(guān)閉控件特定數(shù)據(jù)庫(kù)連接。對(duì)于頁(yè)自身,使用該事件來(lái)執(zhí)行最后清理工作,如:關(guān)閉打開(kāi)的文件和數(shù)據(jù)庫(kù)連接,或完成日志記錄或其他請(qǐng)求特定任務(wù)??偠灾?,Unload就是進(jìn)行最后的清理工作,釋放資源。 總體概覽一篇文章下來(lái),已耗費(fèi)了好多時(shí)間,如果你覺(jué)得對(duì)你有用,那就麻煩點(diǎn)個(gè)推薦吧。如果你覺(jué)得本文很爛,那點(diǎn)個(gè)反對(duì)也是可以的。后面Part 5會(huì)探秘ASP.NET MVC的頁(yè)面生命流程,今天就此停筆,謝謝! 參考資料(1)農(nóng)村出來(lái)的大學(xué)生,《ASP.NET網(wǎng)頁(yè)請(qǐng)求處理全過(guò)程(反編譯)》:http://www.cnblogs.com/poorpan/archive/2011/09/25/2190308.html (2)我自己,《【翻譯】ASP.NET應(yīng)用程序和頁(yè)面聲明周期》:http://www.cnblogs.com/edisonchou/p/3958305.html (3)Shivprasad koirala,《ASP.NET Application and Page Life Cycle》:http://www./Articles/73728/ASP-NET-Application-and-Page-Life-Cycle (4)碧血軒,《ASP.NET頁(yè)面生命周期》:http://www.cnblogs.com/xhwy/archive/2012/05/20/2510178.html (5)木宛城主,《ASP.NET那點(diǎn)不為人知的事兒》:http://www.cnblogs.com/OceanEyes/archive/2012/08/13/aspnetEssential-1.html (6)千年老妖,《ASP.NET頁(yè)面生命周期》:http://www.cnblogs.com/hanwenhuazuibang/archive/2013/04/07/3003289.html (7)MSDN,《Page事件》:http://msdn.microsoft.com/zh-cn/library/system.web.ui.page_events(v=vs.80).aspx 偶像的歌PS:背景音樂(lè) from 張國(guó)榮 電影英雄本色中的插曲 《當(dāng)年情》
作者:周旭龍 出處:http://edisonchou.cnblogs.com/ 本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁(yè)面明顯位置給出原文鏈接。 |
|
來(lái)自: 天使之翼 ` > 《待分類(lèi)》