從.Net類庫代碼來看Asp.net運行時

來源:互聯網
上載者:User
寫在前面的話:網上講Asp.net運行模式的好文章已經很多了,筆者本不用多此一舉,另成一文。但從筆者自己的學習經驗看,如果學到的這些知識不能對應到類庫中的原始碼,印象總歸不夠深刻,大有隔靴搔癢之感。只好自己寫上一篇,對這方面的知識做個小小的總結。文中所有內容都是筆者在看了網上很多文章後,結合自己的開發經驗得出的一些理解,難免有錯誤的地方,歡迎批評指出。另外,由於筆者能力所限,很多地方並未說透(真正對應到代碼),也盼高手能夠給予補充。

一.進入Asp.net運行時之前

  雖然本文的重點是對Managed 程式碼的解析,但為了整個知識點的完整性,這裡簡單介紹一下IIS處理請求的一些基本情況。在一個IIS伺服器上,你可以設定多個應用程式集區(每個應用程式集區可以單獨設定允許使用的最大記憶體數量、CPU使用率、回收背景工作處理序的時間間隔等參數,而且一個應用程式集區裡面只能使用一個版本的.NET Framework),然後把自己的Web應用分別部署到這些應用程式集區中。在預設情況下,每個應用池會有一個背景工作處理序w3wp.exe來維護(如果開通了Web園功能,也可以設定多個背景工作處理序)。每個應用程式(虛擬目錄)在池中都有自己的應用程式定義域,這些應用程式定義域都處於這個應用程式集區的背景工作處理序的進程空間內。

  IIS是通過各種ISAPI的擴充來處理各種類型的應用的。當我們從用戶端提交一個請求過來之後,IIS會根據請求的頁面或者服務的類型,把請求映射到指定的ISAPI擴充。比方說,如果我們需要讓IIS支援perl這樣的伺服器端程式(當然,這個移植工作早就有人做過了),我們就需要編寫一個專門處理對perl頁面進行的請求的ISAPI擴充。根據ISAPI的定義(符合這個定義的ISAPI擴充才能和IIS正常互動),在你的擴充中可以包括ISAPI Extension和ISAPI Filter兩大部分。ISAPI Extension是對請求的處理常式,完成和web伺服器之間的輸入輸出;而ISAPI Filter則是一些回調介面,你可以通過實現這些介面來介入到整個請求處理的每一步驟,對Authentication,RevolveCache等環節進行控制。另外,ISAPI本身就是在背景工作處理序裡啟動並執行,而asp.net運行時也是在背景工作處理序裡啟動並執行,所以兩者的互動非常有效率。

  對於.aspx頁面,這個擴充就是aspnet_isapi.dll。因為這些ISAPI都是非託管的Win32應用,直接對它們進行改動是比較困難的。所以,為了增強Asp.net運行時的可擴充性,aspnet_isapi.dll本身的功能非常少,我們可以把aspnet_isapi.dll簡單理解為請求資訊的路由器,負責把請求從IIS傳送到asp.net運行時。而後面我們將要講到的HttpHandle和HttpModule則分別擔負起了ISAPI Extension和ISAPI Filter的功能,幸運的是,HttpHandle和HttpModule可以由純的Managed 程式碼來實現。

二.從Unmanaged 程式碼到Managed 程式碼

  前面說了,aspnet_isapi.dll是Unmanaged 程式碼,而asp.net運行時是Managed 程式碼,他們都運行在w3wp.exe背景工作處理序裡面,那麼兩者之間的調用點發生在什麼地方呢?在介紹接下來的內容之前必須先介紹一個概念:ECB。ECB的全稱是Extension Control Block,它是一個非託管資源套件,具有對ISAPI介面完整的訪問能力,包含了所有和一個傳入請求有關的底層資訊,如提交的標單中的資料等等。所以說,asp.net中的Managed 程式碼想要訪問aspnet_isapi.dll對外提供的介面,就需要通過ECB。其實更準確的來說,是Managed 程式碼公布了一個IUnknown類型的介面供aspnet_isapi.dll調用,而aspnet_isapi.dll在調用的時候會把自己的ecb地址傳進去。

  明白了ECB的概念,下面我們要介紹一個介面和一個介面的實作類別(位於System.Web.Hosting名字空間下),請讀者注意筆者在代碼中的注釋(本文的主要目的就是和大家一起從代碼實現的角度來認識整個Asp.net運行時,所以代碼裡的注釋是筆者添加的關鍵性說明,後面的所有程式碼片段都是這樣):

IISAPIRuntime
1/**//*InterfaceType(ComInterfaceType.InterfaceIsIUnknown)
  指明了這個介面將作為 IUnknown 派生介面向 COM 公開,這就使得isapi.dll可以以COM方式調用此介面。

*/
2[ComImport, Guid("08a2c56f-7c16-41c1-a8be-432917a1a2d1"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
3public interface IISAPIRuntime
4{
5 void StartProcessing();
6 void StopProcessing();
7 /**//*ProcessRequest方法就是整個處理流程中Managed 程式碼和Unmanaged 程式碼的分界點,可以看到裡面是以一個IntPtr結構傳入了調用方(也就是isapi.dll)的ECB地址*/
8 [return: MarshalAs(UnmanagedType.I4)]
9 int ProcessRequest([In] IntPtr ecb, [In, MarshalAs(UnmanagedType.I4)] int useProcessModel);
10 void DoGCCollect();
11}
12
13/**//*這個類實現了IISAPIRuntime介面。它的執行個體對象存在於每一個AppDomain中,作為整個Asp.net運行時的入口。*/
14public sealed class ISAPIRuntime : MarshalByRefObject, IISAPIRuntime, IRegisteredObject
15{
16 // Fields
17 private static int _isThisAppDomainRemovedFromUnmanagedTable;
18 private static string s_thisAppDomainsIsapiAppId;
19
20 // Methods
21 [AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.Minimal), SecurityPermission(SecurityAction.Demand, Unrestricted=true)]
22 public ISAPIRuntime();
23 public void DoGCCollect();
24 public override object InitializeLifetimeService();
25 /**//*處理請求的進入點方法,由isapi.dll以COM方式調用*/
26 public int ProcessRequest(IntPtr ecb, int iWRType);
27 internal static void RemoveThisAppDomainFromUnmanagedTable();
28 internal void SetThisAppDomainsIsapiAppId(string appId);
29 public void StartProcessing();
30 public void StopProcessing();
31 void IRegisteredObject.Stop(bool immediate);
32}

  所以,一切都是從aspnet_isapi.dll以COM方式調用了一個ISAPIRuntime對象的ProcessRequest方法開始的。可以多提一句的是,這種調用是非同步,也就是說,aspnet_isapi.dll在調用後會立即返回,但ECB會一直保留下來,直到整個請求被處理完畢之後再釋放。

  好,現在我們知道了ISAPIRuntime對象是Managed 程式碼的進入點,那麼這個對象是什麼時候產生的呢?換句話說,w3wp也是一個Unmanaged 程式碼寫出的程式,它是在什麼時候把.net運行時載入進來的呢?(如果好奇心再強一點,還可以問一問一個背景工作處理序是什麼時刻產生並開始啟動並執行,它和應用程式集區有著怎樣的互動。)完全解釋清楚這些問題已經超過了筆者目前的能力範圍,還望高人補充或提供資料線索。但目前我們從.net的代碼中應該可以推斷出,ISAPIRuntime對象和應用程式定義域是對應的,.net在建立應用程式定義域的時候,就會建立ISAPIRuntime對象,見下面的建立應用程式定義域的代碼:

建立應用程式定義域

1/**//*這是System.Web.Hosting.AppDomainFactory類型的Create方法,它調用的是實際工廠的Create方法。*/
2[return: MarshalAs(UnmanagedType.Interface)]
3public object Create(string module, string typeName, string appId, string appPath, string strUrlOfAppOrigin, int iZone)
4{
5 /**//*實際工廠是一個AppManagerAppDomainFactory類型的對象。*/
6 return this._realFactory.Create(appId, appPath);
7}
8
9/**//*AppManagerAppDomainFactory.Create方法,請看代碼內的注釋。*/
10[return: MarshalAs(UnmanagedType.Interface)]
11public object Create(string appId, string appPath)
12{
13 object obj2;
14 try
15 {
16 if (appPath[0] == '.')
17 {
18 FileInfo info = new FileInfo(appPath);
19 appPath = info.FullName;
20 }
21 if (!StringUtil.StringEndsWith(appPath, '\\'))
22 {
23 appPath = appPath + @"\";
24 }
25 ISAPIApplicationHost appHost = new ISAPIApplicationHost(appId, appPath, false);
26 /**//*這個方法內部的調用鏈非常複雜,它一方面建立了一個應用程式定義域,一方面返回一個ISAPIRuntime對象。具體這個方法究竟是如何建立AppDomain對象的,大家可以用
27 JetBrain來跟蹤其調用棧。關於這部分內容更詳盡的資訊,可參見ASP.NET Internals - The bridge between ISAPI and Application Domains一文。
28 另外,如果您使用JetBrain來調試系統程式集的話,有可能會因為缺少相應pdb檔案而不能查看完整調試資訊,這裡提供一個根據已有程式集,先反組譯碼成中間碼,
29 再重新以偵錯模式產生dll和pdb檔案的方法:
30 1)產生IL檔案: ildasm /tok /byt system.web.dll /out=system.web.il
31 2)重建PDB/DLL: ilasm system.web.il /DEBUG /DLL /OUTPUT=System.Web.dll*/
32 ISAPIRuntime o = (ISAPIRuntime) this._appManager.CreateObjectInternal(appId, typeof(ISAPIRuntime), appHost, false, null);
33 o.SetThisAppDomainsIsapiAppId(appId);
34 o.StartProcessing();
35 obj2 = new ObjectHandle(o);
36 }
37 catch (Exception)
38 {
39 throw;
40 }
41 return obj2;
42}

三.Asp.net運行時,我們等待已久的純Managed 程式碼環境

  經過上面長久的鋪墊,我們終於進入了Managed 程式碼的領域。經過前面的內容,我們知道,在Managed 程式碼中首先被執行的是一個ISAPIRuntime對象的ProcessRequest方法,那麼下面我們就來看一看這個方法主要做了些什麼:

ISAPIRuntime.ProcessRequest
1/**//*ISAPIRuntime的方法,處理請求的入口。*/
2public int ProcessRequest(IntPtr ecb, int iWRType)
3{
4 try
5 {
6 /**//*這裡ecb被作為參數傳入,返回一個HttpWorkerRequest類型的對象,作為對一個請求的資料的封裝。但HttpWorkerRequest
7 *只是一個抽象基類,CreateWorkerRequest作為一個Factory 方法,返回的實際類型是ISAPIWorkerRequestInProc,
8 *ISAPIWorkerRequestInProcForIIS6或ISAPIWorkerRequestOutOfProc。這些類型裡面提供的方法,其實大多
9 *圍繞著如何從ecb中去擷取資料,所以都包含了很多對System.Web.UnsafeNativeMethods類型中靜態方法的調用。
10 **/
11 HttpWorkerRequest wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, iWRType);
12 string appPathTranslated = wr.GetAppPathTranslated();
13 string appDomainAppPathInternal = HttpRuntime.AppDomainAppPathInternal;
14 if ((appDomainAppPathInternal == null) || StringUtil.EqualsIgnoreCase(appPathTranslated, appDomainAppPathInternal))
15 {
16 /**//*從這裡開始,對請求的處理流程就交給了HttpRuntime。需要注意的是,ISAPI是多線程的,而且對ProcessRequest的調用是非同步,
17 *這就要求HttpRuntime.ProcessRequest方法是安全執行緒的。看一看HttpRuntime.ProcessRequestNoDemand裡的代碼大家就清楚,
18 *所有的請求會被排成一個隊列,順次執行,保證了並發安全。
19 *最終,HttpRuntime.ProcessRequestInternal方法會被調用,我們接下來就去看看那個方法。
20 **/
21 HttpRuntime.ProcessRequestNoDemand(wr);
22 return 0;
23 }
24 HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged, SR.GetString("Hosting_Phys_Path_Changed", new object[] { appDomainAppPathInternal, appPathTranslated }));
25 }
26 catch (Exception exception)
27 {
28 Misc.ReportUnhandledException(exception, new string[] { SR.GetString("Failed_to_process_request") });
29 throw;
30 }
31 return 1;
32}

  上面的程式碼片段最主要的作用就是調用了HttpRumtime.ProcessRequestInternal方法,下面我們就一起來看看這個方法的實現:

HttpRuntime.ProcessRequestInternal
1/**//*在HttpRuntime.ProcessRequestInternal()方法裡,有如下幾個重要的對象被建立出來:
2 *(1)HttpContext(包括其中的HttpRequest,HttpResponse)
3 *(2)HttpApplication
4 *同時,會執行HttpApplication對象的ProcessRequest方法,
5 */
6private void ProcessRequestInternal(HttpWorkerRequest wr)
7{
8 /**//*HttpContext對象在這裡被建立。HttpWorkerRequest做為構造參數,而HttpWorkerRequest本身
9 *又圍繞著對ecb的處理建立了一群高層的方法,它的執行個體會被HttpContext傳給HttpRequest和HttpResponese
10 *做為他們的構造參數。所以,這裡也能更清楚地看出HttpWorkerRequest作為ecb的託管環境封裝器的實質。
11 *另外,這裡也能清楚地反映出,每一個請求都有一個自己的HttpContext對象(而每一個HttpContext對象都管理著
12 *一個HttpSession對象--參見HttpContext的Session屬性,這也就保證了每個訪問者有自己的session對象。),你可以
13 *使用HttpContext.Current來訪問到這個對象。
14 */
15 HttpContext extraData = new HttpContext(wr, false);
16 wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, extraData);
17 Interlocked.Increment(ref this._activeRequestCount);
18 HostingEnvironment.IncrementBusyCount();
19 try
20 {
21 try
22 {
23 this.EnsureFirstRequestInit(extraData);
24 }
25 catch
26 {
27 if (!extraData.Request.IsDebuggingRequest)
28 {
29 throw;
30 }
31 }
32 extraData.Response.InitResponseWriter();
33 /**//*用應用程式工廠返回一個HttpApplication對象。
34 *和線程池對線程的管理相似,HttpApplicationFactory中以stack維護了一個HttpApplication的列表(參見HttpApplicationFactory
35 *的_freeList變數)。在這句方法調用的最後,實際是調用了 _theApplicationFactory.GetNormalApplicationInstance(context),
36 *裡面就是從_freeList的棧頂pop出一個已經構造的HttpApplication執行個體。
37 *所以,對於每一個請求,由HttpContext作為上下文,由一個HttpApplication對象來控制整個應用處理的pipeline,整個
38 *處理過程是在由背景工作處理序管理的線程池中的某個線程內完成的。
39 *另外,在一個應用程式定義域內,由於可以同時處理多個請求,所以就有多個HttpApplication執行個體和多個活動線程(您可以使用windbg的sos
40 *擴充來觀察它們之間的關係,本文就不繼續深入了)。
41 *還有,對所有HttpModules的載入就是發生在HttpApplication對象的建立過程之中(包括系統已經提供的Authentication等模組兒和我們
42 *的自訂模組)。我們可以在Web.config裡聲明自己的自訂模組。這些模組的作用就是在整個HttpApplication處理管線的相關事件點上,
43 *掛上自己的處理。注意一下IHttpModule介面的Init()方法的聲明,這個方法的傳入參數就是要被建立的HttpApplication對象,所以,如果
44 *你自己的模組想在緩衝讀取上加入一些自訂動作,你只需進行如下處理即可:
45 public class YourCustomModule : IHttpModule
46 {
47 public void Init(HttpApplication application)
48 {
49 application.ResolveRequestCache += new EventHandler(this.YourCustomResolveRequestCache);
50 }
51 }
52 *另外,通過對HttpApplicationFactory.GetApplicationInstance方法內部實現方式的閱讀,你會發現在每一個HttpApplication對象被建立
53 *之後,會立刻調用這個對象的InitInternal方法,而這個方法裡面做了很多重要的初始化操作,內容較多,我們將在下文中單獨介紹。
54 *
55 */
56 IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(extraData);
57 if (applicationInstance == null)
58 {
59 throw new HttpException(SR.GetString("Unable_create_app_object"));
60 }
61 if (EtwTrace.IsTraceEnabled(5, 1))
62 {
63 EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, extraData.WorkerRequest, applicationInstance.GetType().FullName, "Start");
64 }
65 /**//*看一下System.Web.HttpApplication的型別宣告
66 *public class HttpApplication : IHttpAsyncHandler, IHttpHandler, IComponent, IDisposable
67 *你會發現它同時實現了同步和非同步IHandler,所以在預設情況下,Asp.net對請求的處理是非同步。
68 */
69 if (applicationInstance is IHttpAsyncHandler)
70 {
71 IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance;
72 extraData.AsyncAppHandler = handler2;
73 /**//*BeginProcessRequest會調用HttpApplication的ResumeSteps()方法,在ResumeSteps()中完成了整個應用程式周期的所有操作,
74 *包括對所有事件的觸發、執行,對Handler的調用等。後文會有專門的小節進行介紹。*/
75 handler2.BeginProcessRequest(extraData, this._handlerCompletionCallback, extraData);
76 }
77 else
78 {
79 applicationInstance.ProcessRequest(extraData);
80 this.FinishRequest(extraData.WorkerRequest, extraData, null);
81 }
82 }
83 catch (Exception exception)
84 {
85 extraData.Response.InitResponseWriter();
86 this.FinishRequest(wr, extraData, exception);
87 }
88}

  從上面的代碼可以看出,HttpApplication對象是經由HttpApplicationFactory.GetApplicationInstance(並最終調用HttpRuntime.CreateNonPublicInstance)建立的。
  HttpApplicationFactory對象會先解譯目錄中的Global.asax檔案,接著載入虛擬目錄內的Application Assembly(Global.dll),而後合并兩者建立出一個Ghost Application Class,最後編譯此Class後取得對象執行個體後返回至HttpRuntime對象,這個對象執行個體就是HttpApplication對象。解譯與編譯.asax檔案的動作只發生於此虛擬目錄第一次處理使用者要求,或是Global.asax、Global.dll在前次執行後又做了變動時。至於HttpRuntime.CreateNonPublicInstance方法內部到底是通過什麼樣的代碼,先後調用了Parser和Compiler,根據machine.config和web.config來產生一個HttpApplication對象,筆者未能通過自己的努力追蹤到,盼高手補充。

  總之,通過machine.config及web.config檔中對<httpHandlers>節點的說明,HttpApplication可以從HanlerFactory的緩衝池HandlerFactoryCache中,根據當前請求的頁面類型名稱,獲得所需的HandlerFactory。對於我們通常的aspx頁面,返回的將是PageHandlerFactory(HttpApplication通過GetFactory()方法返回它),而PageHandlerFactory將接下來完成對所請求頁面的Parser,並返回一個代表所請求頁面的Page類:
Page page = BuildManager.CreateInstanceFromVirtualPath(virtualPath, typeof(Page), context, true, true) as Page;
上面這句代碼是被PageHandleFactory的GetHandle方法間接調用的(大家可以從HttpApplication.MapHttpHandler方法),調用返回的page對象是一個非常非常關鍵的執行個體(具體的方法調用過程中應當包括了對PageParser和PageBuilder的調用,望高手補充),因為它就是我們普通的aspx頁面處理流程中,那個扮演著IHttpHandler的角色!也正因為此,我們在HttpRuntime.ProcessRequestInternal()方法裡看到的applicationInstance.ProcessRequest(extraData)調用,實際上是調用的一個System.Web.UI.Page類型執行個體的ProcessRequest方法,整個執行流也因此進入了Page.ProcessRequestMain()的裡面。我們平常所說的一個頁面的生命週期的若干事件,您只要好好看看這個方法的實現,就都能明白了。由於這個方法估計是大家平時看的比較多的,對之也比較熟悉,本文這裡就不多解釋了。

四.HttpApplication的事件機制

  到上面介紹的內容為止,整個處理流程基本上就講完了。但如果只介紹到這裡,恐怕大家對HttpApplication, IHttpModule, IHttpHandler三者的關係還是不太清楚。一個請求過程的所有事件(如BeginRequest、AuthenticateRequest等)是如何被觸發的?如何通過自己的自訂Module來處理這些事件?Handler的處理又是在什麼位置切入的?其實,這一切都是以HttpApplication內建的事件機製為核心的,下面就讓我們來一步步揭示出它的實現方式(對.net中事件機制不夠熟悉的讀者可以先參看筆者另一篇文章:Part I of Events in Asp.Net: Events in .Net)。這裡面涉及的事件很多,我們就以BeginRequest這個事件為例來說明:
(1)首先當然是要對事件本身進行定義
public event EventHandler BeginRequest
(2)由於有不止一個事件,為了方便對所有事件的管理,給每一個事件定義了一個唯一key,用作在事件容器中尋找指定事件的標誌
private static readonly object EventBeginRequest;
(3)把對一個個事件觸發定義為一個個“執行步驟的執行”,下面是對“執行步驟”這個介面的定義
internal interface IExecutionStep
{
// 每一個“執行步驟”的執行命令
void Execute();
// Properties
bool CompletedSynchronously { get; }
bool IsCancellable { get; }
}

  這裡為什麼要看似多此一舉把對事件的觸發再做一層封裝,封裝為所謂“執行步驟”,在後文介紹。
(4)用一個數組來儲存所有的執行步驟。可以想象,真正到執行的時候,從數組裡取出每一項IExecutionStep,再執行其Execute()方法即可。而Execute裡面,肯定是對事件委託鏈的調用無疑
private IExecutionStep[] _execSteps;
(5)HttpModule在它初始化的時候完成對事件的註冊。在建立HttpApplication對象的時候,會調用這個對象的InitInternal方法,這個方法內部會調用InitModules() ,它用來初始化和這個應用有關的所有HttpModule,而在這個方法裡面,最重要的就是調用了每個Module的Init()方法。那麼,如果我們有一個自訂的HttpModule,並希望這個Module去響應BeginRequest事件,我們應該這樣定義自己的Module的Init()方法:
public void Init(HttpApplication application)
{
application.BeginRequest += new EventHandler(this.YourCustomMethodForBeginRequestEvent);
}

  這樣就完成了註冊。當然,此時事件還沒有執行,你也還沒有看到事件和執行步驟的關係,HttpHandler也尚未登場。

  (6)前面提到過所謂“執行步驟”,讓我們先來看兩個在HttpApplication中被定義的IExecutionStep,因為這兩個執行步驟中所完成的,一個是對HttpHandler的解析和執行個體化,一個是調用HttpHandler的ProcessRequest方法,他們分別是MapHandlerExecutionStep和CallHandlerExecutionStep。讀者可以自己去讀一下這兩個類的Execute()方法的代碼,當您第一次從CallHandlerExecutionStep.Execute()中看到handler.ProcessRequest(context)和handler2.BeginProcessRequest(context, this._completionCallback, null)這樣的調用語句,從MapHandlerExecutionStep.Execute()中看到context.Handler = this._application.MapHttpHandler(context, request.RequestType, request.FilePathObject, request.PhysicalPathInternal, false); 這樣的調用語句,一定會有一種“哎呀,原來你們在這裡”的快感,呵呵。

  (7)對“執行步驟數組”的初始化操作,就放在HttpApplication.InitInternal()方法裡,具體就是下面這段語句:

執行步驟的初始化
1this.CreateEventExecutionSteps(EventBeginRequest, steps);
2this.CreateEventExecutionSteps(EventAuthenticateRequest, steps);
3this.CreateEventExecutionSteps(EventDefaultAuthentication, steps);
4this.CreateEventExecutionSteps(EventPostAuthenticateRequest, steps);
5this.CreateEventExecutionSteps(EventAuthorizeRequest, steps);
6this.CreateEventExecutionSteps(EventPostAuthorizeRequest, steps);
7this.CreateEventExecutionSteps(EventResolveRequestCache, steps);
8this.CreateEventExecutionSteps(EventPostResolveRequestCache, steps);
9steps.Add(new MapHandlerExecutionStep(this));
10this.CreateEventExecutionSteps(EventPostMapRequestHandler, steps);
11this.CreateEventExecutionSteps(EventAcquireRequestState, steps);
12this.CreateEventExecutionSteps(EventPostAcquireRequestState, steps);
13this.CreateEventExecutionSteps(EventPreRequestHandlerExecute, steps);
14steps.Add(new CallHandlerExecutionStep(this)); //從這裡,您可以很容易看到Handler對頁面的解析處理在整個請求的處理的什麼位置
15 //有哪些應用程式事件在它之前,哪些在它之後
16this.CreateEventExecutionSteps(EventPostRequestHandlerExecute, steps);
17this.CreateEventExecutionSteps(EventReleaseRequestState, steps);
18this.CreateEventExecutionSteps(EventPostReleaseRequestState, steps);
19steps.Add(new CallFilterExecutionStep(this));
20this.CreateEventExecutionSteps(EventUpdateRequestCache, steps);
21this.CreateEventExecutionSteps(EventPostUpdateRequestCache, steps);
22this._endRequestStepIndex = steps.Count;
23this.CreateEventExecutionSteps(EventEndRequest, steps);
24steps.Add(new NoopExecutionStep());
25this._execSteps = new IExecutionStep[steps.Count];
26steps.CopyTo(this._execSteps); //把整個數組拷貝給HttpApplication的私人變數_execSteps可以看到,對執行步驟數組的初始化有兩種形式,一種是直接添加一個步驟:

steps.Add(new MapHandlerExecutionStep(this));

  這種執行步驟是和事件無關的,只是在整個事件流中的特定位置執行一些特定的操作(執行個體化Handler之類的)。

  而另一種是要把相關事件的處理方法列表添加到步驟中,每一個步驟其實是對一個事件的處理:

this.CreateEventExecutionSteps(EventBeginRequest, steps);

  (8)執行步驟數組的執行。

  其實在前文中已經提到過了,整個執行步驟數組的執行,是在HttpApplication.ResumeSteps()中調用的。恐怕就算不看這個方法的代碼,大家也能想象得出,它是遍曆整個執行步驟數組,然後調用其中每一項的Execute方法。這裡大家大概也就清楚了,為什麼會有一個執行步驟的概念。以筆者看來,首先它在概念上很好理解,完全貼合整個應用處理的管道模型pipeline;第二它屏蔽了引發事件的應用執行步驟和普通的內建執行步驟之間的差別;第三是比較容易在以後對整個流程進行改進和擴充。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.