文章內容
繼續上一章節的內容,通過HttpApplicationFactory的GetApplicationInstance靜態方法獲取例項,然後執行該例項的BeginProcessRequest方法進行執行餘下的Http Pipeline 操作,程式碼如下:
// Get application instance IHttpHandler app = HttpApplicationFactory.GetApplicationInstance(context);
那GetApplicationInstance這個方法究竟做了啥呢?難道只是new一個新物件出來?感覺應該不像,那我們就來看看HttpApplicationFactory類的GetApplicationInstance靜態方法原始碼:
internal static IHttpHandler GetApplicationInstance(HttpContext context) { if (_customApplication != null) return _customApplication; // Check to see if it's a debug auto-attach request if (context.Request.IsDebuggingRequest) return new HttpDebugHandler(); _theApplicationFactory.EnsureInited(); _theApplicationFactory.EnsureAppStartCalled(context); return _theApplicationFactory.GetNormalApplicationInstance(context); }
裡面有3行程式碼我已經標記為粗體了,在解釋每行程式碼的具體作用之前,先看看_theApplicationFactory物件例項從哪裡來,通過檢視該欄位的宣告程式碼可以看到它是單例的實現。
// the only instance of application factory private static HttpApplicationFactory _theApplicationFactory = new HttpApplicationFactory();
第一行粗體程式碼是執行,該例項的EnsureInited方法,這個方法會通過lock的方式呼叫Init方法(好處自然不用多說了吧),程式碼如下:
private void EnsureInited() { if (!_inited) { lock (this) { if (!_inited) { Init(); _inited = true; } } } }
通過查詢 Init方法的程式碼以及其中2行如下程式碼裡的細節,我們可以得知,這2行程式碼主要是從global.asax獲取內容,然後進行編譯。
_appFilename = GetApplicationFile();
CompileApplication();
所以,HttpApplicationFactory._theApplicationFactory.EnsureInited() 的方法首先檢查HttpApplicationFactory是否被初始化,如果沒有,就通過HttpApplicationFactory.Init()進行初始化。在Init()中,先獲取global.asax檔案的完整路徑,然後呼叫CompileApplication()對global.asax進行編譯。
第2行粗體的EnsureAppStartCalled方法,最終會呼叫如下的私有方法FireApplicationOnStart,程式碼如下:
private void FireApplicationOnStart(HttpContext context) { if (_onStartMethod != null) { HttpApplication app = GetSpecialApplicationInstance(); app.ProcessSpecialRequest( context, _onStartMethod, _onStartParamCount, this, EventArgs.Empty, null); RecycleSpecialApplicationInstance(app); } }
通過程式碼我們能夠得知HttpApplicationFactory._theApplicationFactory.EnsureAppStartCalled(context) 建立特定的HttpApplication例項,觸發ApplicationOnStart事件,執行ASP.global_asax中的Application_Start(object sender, EventArgs e)方法。然後在處理完事件以後就立即被回收掉,因為系統初始化只需要一次,但是其中的GetSpecialApplicationInstance裡會對IIS7做一些特殊的事情,我們後面的章節會講到。
第3行的粗體程式碼是我們這裡要說的重點,它方法裡的程式碼如下:
private HttpApplication GetNormalApplicationInstance(HttpContext context) { HttpApplication app = null; lock (_freeList) { if (_numFreeAppInstances > 0) { app = (HttpApplication)_freeList.Pop(); _numFreeAppInstances--; if (_numFreeAppInstances < _minFreeAppInstances) { _minFreeAppInstances = _numFreeAppInstances; } } } if (app == null) { // If ran out of instances, create a new one app = (HttpApplication)HttpRuntime.CreateNonPublicInstance(_theApplicationType); using (new ApplicationImpersonationContext()) { app.InitInternal(context, _state, _eventHandlerMethods); } } return app; }
如果在有空閒的HttpApplication例項,就直接用,如果沒有就新建立,然後呼叫InitInternal方法進行初始化相關的內容,最後返回該HttpApplication例項。
讓我們來看看HttpApplication的核心方法InitInternal裡都是幹了什麼事兒吧,先上程式碼,有點多,但是很值得:
internal void InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers) { Debug.Assert(context != null, "context != null"); // Remember state _state = state; PerfCounters.IncrementCounter(AppPerfCounter.PIPELINES); try { try { // Remember context for config lookups _initContext = context; _initContext.ApplicationInstance = this; // Set config path to be application path for the application initialization context.ConfigurationPath = context.Request.ApplicationPathObject; // keep HttpContext.Current working while running user code using (new DisposableHttpContextWrapper(context)) { // Build module list from config if (HttpRuntime.UseIntegratedPipeline) { Debug.Assert(_moduleConfigInfo != null, "_moduleConfigInfo != null"); Debug.Assert(_moduleConfigInfo.Count >= 0, "_moduleConfigInfo.Count >= 0"); try { context.HideRequestResponse = true; _hideRequestResponse = true; InitIntegratedModules(); } finally { context.HideRequestResponse = false; _hideRequestResponse = false; } } else { InitModules(); // this is used exclusively for integrated mode Debug.Assert(null == _moduleContainers, "null == _moduleContainers"); } // Hookup event handlers via reflection if (handlers != null) HookupEventHandlersForApplicationAndModules(handlers); // Initialization of the derived class _context = context; if (HttpRuntime.UseIntegratedPipeline && _context != null) { _context.HideRequestResponse = true; } _hideRequestResponse = true; try { Init(); } catch (Exception e) { RecordError(e); } } if (HttpRuntime.UseIntegratedPipeline && _context != null) { _context.HideRequestResponse = false; } _hideRequestResponse = false; _context = null; _resumeStepsWaitCallback= new WaitCallback(this.ResumeStepsWaitCallback); // Construct the execution steps array if (HttpRuntime.UseIntegratedPipeline) { _stepManager = new PipelineStepManager(this); } else { _stepManager = new ApplicationStepManager(this); } _stepManager.BuildSteps(_resumeStepsWaitCallback); } finally { _initInternalCompleted = true; // Reset config path context.ConfigurationPath = null; // don't hold on to the context _initContext.ApplicationInstance = null; _initContext = null; } } catch { // Protect against exception filters throw; } }
該程式碼主要有2個功能,一個是初始化大家熟悉的HttpModules,一個是通過BuildSteps執行20多個生命週期事件的處理函式(這部分內容,我們將在下一章節詳細講解Http Pipeline)。通過上面的程式碼我們可以看出,每個功能都有一個特殊判斷,判斷IIS是否是IIS7的整合模式,如果是就有特殊的步驟,如果不是就走一般的步驟,兩者直接的差異分別是:IIS7初始化HttpModules的時候會從網站配置的Modules裡讀取(因為IIS7預載入CLR和大批量Modules),BuildSteps的時候, IIS7整合模式走的是自己特殊的流程(載入伺服器上的HttpModules)。
讓我們先總結一下再看程式碼,InitInternal方法的主要功能如下:
- InitModules():根據Web.Config的設定,載入相應的HttpModules。
- InitIntegratedModules():會載入IIS7整合模式下在伺服器上設定的HttpModuels和Web.config裡system.webserver下的HttpModuels。
- HookupEventHandlersForAppplicationAndModules:根據發生的事件,呼叫HttpApplication例項中相應的事件處理函式。
- 建立很多實現IExecutionStep介面的類的例項並新增到當前HttpApplication例項的_execSteps中,等待回撥時執行。從這裡我們可以看到HttpApplication是以非同步的方式處理請求, 對請求的很多處理工作都放入了_execStep等待回撥時執行。
至此,除了20多個週期事件和Handler相關的程式碼我們沒有講解,其它和HttpApplication相關的並且對我們有幫助的,已經差不多清晰了。關於20多個週期事件和執行Handler方面的內容,我們下一章節再做詳細解釋。
參考資料:
http://msdn.microsoft.com/en-us/magazine/cc188942.aspx
http://msdn.microsoft.com/en-us/library/bb470252.aspx
http://www.cnblogs.com/zhaoyang/archive/2011/11/16/2251200.html
同步與推薦
本文已同步至目錄索引:MVC之前的那點事兒系列
MVC之前的那點事兒系列文章,包括了原創,翻譯,轉載等各型別的文章,如果對你有用,請推薦支援一把,給大叔寫作的動力。