IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(context);
上一篇說到了創建 HttpApplication 對象出來(就是上面這句話了), 那么在創建對象的時候, 做了些什么事情呢, 是不是只是創建了對象而已呢, 繼續分析.
internal static IHttpHandler GetApplicationInstance(HttpContext context) { if (_customApplication != null) { return _customApplication; } if (context.Request.IsDebuggingRequest) { return new HttpDebugHandler(); } _theApplicationFactory.EnsureInited(); _theApplicationFactory.EnsureAppStartCalled(context); return _theApplicationFactory.GetNormalApplicationInstance(context); }
1. 先來看一下EnsureInited方法, 看看里面做了些什么.
private void EnsureInited() { if (!this._inited) { lock (this) { if (!this._inited) { this.Init(); this._inited = true; } } } }
加鎖的方式, 初始化了一些東西, 但是從這里并不能看出是初始化了什么, 那只能再進去看Init()方法了.
private void Init() { if (_customApplication == null) { try { try { this._appFilename = GetApplicationFile(); this.CompileApplication(); } finally { this.SetupChangesMonitor(); } } catch { throw; } } }
大叔在這一步就告訴我們, 是從global.asax獲取內容, 進行編譯. 但是并不能直觀看到文件, 所以, 我決定再進一步瞧瞧.
1.1 GetApplicationFile 方法
internal static string GetApplicationFile() { return Path.Combine(HttpRuntime.AppDomainAppPathInternal, "global.asax"); }
這一下, 就非常清晰的能看到, 確實出現了global.asax了. 到這里, 我才能確定, 大叔說的正確性. 也就是說, 這個方法, 其實是返回 global.asax文件的路徑的.
OK, 既然已經拿到文件了, 那么在看一下, 接下來做了什么.
1.2 CompileApplication 方法 -- 編譯Global.asax文件, 提取其中重要的信息
private void CompileApplication() {
//這里返回的是Global.asax中繼承 HttpApplication類的類型, MVC模式下, 這里返回的是 typeof(MvcApplication)
//如果查找不到, 則會返回 typeof(HttpApplication) this._theApplicationType = BuildManager.GetGlobalAsaxType(); BuildResultCompiledGlobalAsaxType globalAsaxBuildResult = BuildManager.GetGlobalAsaxBuildResult(); if (globalAsaxBuildResult != null) { if (globalAsaxBuildResult.HasAppOrSessionObjects) { this.GetAppStateByParsingGlobalAsax(); } this._fileDependencies = globalAsaxBuildResult.VirtualPathDependencies; } if (this._state == null) { this._state = new HttpApplicationState(); } this.ReflectOnApplicationType(); }
1.2.1 繼續看里面的 ReflectOnApplicationType 方法
private void ReflectOnApplicationType() { ArrayList list = new ArrayList(); foreach (MethodInfo info in this._theApplicationType.GetMethods(
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance)) { if (this.ReflectOnMethodInfoIfItLooksLikeEventHandler(info)) { list.Add(info); } } Type baseType = this._theApplicationType.BaseType; if ((baseType != null) && (baseType != typeof(HttpApplication))) { foreach (MethodInfo info2 in baseType.GetMethods(
BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance)) { if (info2.IsPrivate && this.ReflectOnMethodInfoIfItLooksLikeEventHandler(info2)) { list.Add(info2); } } } this._eventHandlerMethods = new MethodInfo[list.Count]; for (int i = 0; i < this._eventHandlerMethods.Length; i++) { this._eventHandlerMethods[i] = (MethodInfo) list[i]; } }
接下來, 可以看到激動人心的代碼了. 是什么呢? 答案即將揭曉.
1.2.1.1 ReflectOnMethodInfoIfItLooksLikeEventHandler方法
//HttpApplicationFactory類中的私有方法
private bool ReflectOnMethodInfoIfItLooksLikeEventHandler(MethodInfo m) { ParameterInfo[] parameters; string str; if (m.ReturnType == typeof(void)) { parameters = m.GetParameters(); switch (parameters.Length) { case 0: goto Label_0089; case 2: if (!(parameters[0].ParameterType != typeof(object))) { if ((parameters[1].ParameterType != typeof(EventArgs)) && !parameters[1].ParameterType.IsSubclassOf(typeof(EventArgs))) { return false; } goto Label_0089; } return false; } } return false; Label_0089: str = m.Name; int index = str.IndexOf('_'); if ((index <= 0) || (index > (str.Length - 1))) { return false; } if (StringUtil.EqualsIgnoreCase(str, "Application_OnStart")
|| StringUtil.EqualsIgnoreCase(str, "Application_Start")) { this._onStartMethod = m; this._onStartParamCount = parameters.Length; } else if (StringUtil.EqualsIgnoreCase(str, "Application_OnEnd")
|| StringUtil.EqualsIgnoreCase(str, "Application_End")) { this._onEndMethod = m; this._onEndParamCount = parameters.Length; } else if (StringUtil.EqualsIgnoreCase(str, "Session_OnEnd")
|| StringUtil.EqualsIgnoreCase(str, "Session_End")) { this._sessionOnEndMethod = m; this._sessionOnEndParamCount = parameters.Length; } return true; }
看到我標紅的六個名字了么. 是不是很熟悉, 是不是讓人激動.
學過java的人, 可能對 EqualsIgnoreCase 比較熟悉, 這個方法, 是用于字符串的比較的, 忽略大小寫, 比較字符串. 明白這個方法之后, 上面的代碼應該就很清晰了.
好了, 看完這里, 就能明白, 在編譯Global.asax的時候, 會獲取其中的以上幾個方法, 將之存入到 HttpApplicationFactory._eventHandlerMethods中.
1.3 SetupChangesMonitor方法
這個方法, 說實話, 我也沒怎么看懂, 里面出現比較多的字樣是 file changes, 但是就 Monitor字面意思來看, 應該是設置監視器的意思. 具體是干啥的, 暫時還沒有弄清楚. 不過并不影響整個解析過程, 等之后我搞清楚了, 在貼在這里.
那在這里可以得出一個結論了 : HttpApplicationFactory._theApplicationFactory.EnsureInited() 的方法首先檢查HttpApplicationFactory是否被初始化,如果沒有,就通過HttpApplicationFactory.Init()進行初始化。在Init()中,先獲取global.asax文件的完整路徑,然后調用CompileApplication()對global.asax進行編譯。注意哦, 這里是編譯, 并不是執行里面的方法.
2. EnsureAppStartCalled 方法, 還是先貼出完整的代碼
private void EnsureAppStartCalled(HttpContext context) { if (!this._appOnStartCalled) { lock (this) { if (!this._appOnStartCalled) { using (new DisposableHttpContextWrapper(context)) { WebBaseEvent.RaiseSystemEvent(this, 0x3e9); this.FireApplicationOnStart(context); } this._appOnStartCalled = true; } } } }
一堆看不明白的代碼, 沒關系, 抓住重點的幾個方法就可以了. \接下來, 看一下 FireApplicationOnStart 方法.
2.1 FireApplicationOnStart 方法
private void FireApplicationOnStart(HttpContext context) { if (this._onStartMethod != null) { HttpApplication specialApplicationInstance = this.GetSpecialApplicationInstance(); specialApplicationInstance.ProcessSpecialRequest(context,
this._onStartMethod, this._onStartParamCount, this, EventArgs.Empty, null); this.RecycleSpecialApplicationInstance(specialApplicationInstance); } }
通過代碼, 能看到, 這里又創建了一個 特殊的 HttpApplication 實例, 并且用一下之后, 就把他給回收了. 那么在回收之前, 又干了些什么事情呢?
注意到這里的 ProcessSpecialRequest 的參數中, 有一個方法 onStartMethod, 那么只要搞清楚, 這個方法指向誰, 就能知道 ProcessSpecialRequest 方法究竟干了些什么事情.
細心的人可能會發現, onStartMethod 看著有些熟悉, 其實他就是我在 1.2.1.1 中, 代碼標注為綠色中的一個.
2.1.1 ProcessSpecialRequest 方法
這里主要看一下這個方法吧. 因為在這個方法里面, 會調用 onStartMethod方法, 即 Application_Start 方法.
//HttpApplication
internal void ProcessSpecialRequest(HttpContext context, MethodInfo method,
int paramCount, object eventSource, EventArgs eventArgs, HttpSessionState session) { this._context = context; if (HttpRuntime.UseIntegratedPipeline && (this._context != null)) { this._context.HideRequestResponse = true; } this._hideRequestResponse = true; this._session = session; this._lastError = null; using (new DisposableHttpContextWrapper(context)) { using (new ApplicationImpersonationContext()) { try { this.SetAppLevelCulture(); this.InvokeMethodWithAssert(method, paramCount, eventSource, eventArgs); } catch (Exception exception) { Exception innerException; if (exception is TargetInvocationException) { innerException = exception.InnerException; } else { innerException = exception; } this.RecordError(innerException); if (context == null) { try { WebBaseEvent.RaiseRuntimeError(innerException, this); } catch { } } } finally { if (this._state != null) { this._state.EnsureUnLock(); } this.RestoreAppLevelCulture(); if (HttpRuntime.UseIntegratedPipeline && (this._context != null)) { this._context.HideRequestResponse = false; } this._hideRequestResponse = false; this._context = null; this._session = null; this._lastError = null; this._appEvent = null; } } } }
接著看上面這個標紅方法.
[ReflectionPermission(SecurityAction.Assert, Flags=ReflectionPermissionFlag.RestrictedMemberAccess)] private void InvokeMethodWithAssert(MethodInfo method, int paramCount, object eventSource, EventArgs eventArgs) { if (paramCount == 0) { method.Invoke(this, new object[0]); } else { method.Invoke(this, new object[] { eventSource, eventArgs }); } }
在這里看到, 調用方法了. 現在知道 Application_Start的執行時機了.
OK, 到這里, 可以得到我們想要的結論: HttpApplicationFactory._theApplicationFactory.EnsureAppStartCalled(context) 創建特定的HttpApplication實例,觸發ApplicationOnStart事件,執行ASP.global_asax中的Application_Start(object sender, EventArgs e)方法。然后在處理完事件以后就立即被回收掉,因為系統初始化只需要一次.
3. GetNormalApplicationInstance 方法, 代碼如下:
private HttpApplication GetNormalApplicationInstance(HttpContext context) { HttpApplication state = null; lock (this._freeList) { if (this._numFreeAppInstances > 0) { state = (HttpApplication) this._freeList.Pop(); this._numFreeAppInstances--; if (this._numFreeAppInstances < this._minFreeAppInstances) { this._minFreeAppInstances = this._numFreeAppInstances; } } } if (state == null) { state = (HttpApplication) HttpRuntime.CreateNonPublicInstance(this._theApplicationType); using (new ApplicationImpersonationContext()) { state.InitInternal(context, this._state, this._eventHandlerMethods); } } if (AppSettings.UseTaskFriendlySynchronizationContext) { state.ApplicationInstanceConsumersCounter = new CountdownTask(1); state.ApplicationInstanceConsumersCounter.Task.ContinueWith(delegate (Task _, object o) { RecycleApplicationInstance((HttpApplication) o); }, state, TaskContinuationOptions.ExecuteSynchronously); } return state; }
從這個方法可以看出, 并不是每次都會去創建 HttpApplication 實例的, 而是會先去查看是否有空閑的實例, 有的話, 就直接用, 沒有才會去創建一個新的.
在拿到這個實例之后, 調用了 InitInternal 方法. 那么來看一下這個方法里面, 又做了些什么.
internal void InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers) { this._state = state; PerfCounters.IncrementCounter(AppPerfCounter.PIPELINES); try { try { // Remember context for config lookups this._initContext = context; this._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) {
//集成模式下, 走這里, 會跳過 InitModules()方法 try { context.HideRequestResponse = true; this._hideRequestResponse = true; this.InitIntegratedModules(); goto Label_006B; } finally { context.HideRequestResponse = false; this._hideRequestResponse = false; } }
//經典模式下, 才會進這個方法 this.InitModules(); Label_006B: if (handlers != null) { this.HookupEventHandlersForApplicationAndModules(handlers); } this._context = context; if (HttpRuntime.UseIntegratedPipeline && (this._context != null)) { this._context.HideRequestResponse = true; } this._hideRequestResponse = true; try {
//虛方法, 調用MVCApplication的Init方法(如果這個方法存在), 否則調用HttpApplication的Init方法 this.Init(); } catch (Exception exception) { this.RecordError(exception); } } if (HttpRuntime.UseIntegratedPipeline && (this._context != null)) { this._context.HideRequestResponse = false; } this._hideRequestResponse = false; this._context = null; this._resumeStepsWaitCallback = new WaitCallback(this.ResumeStepsWaitCallback);
//Construct the execution steps array if (HttpRuntime.UseIntegratedPipeline) { this._stepManager = new PipelineStepManager(this); } else { this._stepManager = new ApplicationStepManager(this); } this._stepManager.BuildSteps(this._resumeStepsWaitCallback); } finally { this._initInternalCompleted = true;
//Reset config path context.ConfigurationPath = null;
//don't hold on to the context this._initContext.ApplicationInstance = null; this._initContext = null; } } catch { throw; } }
這個方法好長啊, 不過沒關系, 后面會詳細講解. 先從字面意思看, 這里應該是初始化了 HttpModules , 然后通過 BuildSteps 來創建 20多個生命周期事件的處理函數(后面會詳細介紹).
到這里, 我們先總結一下再看代碼,InitInternal方法的主要功能如下:
- InitModules():經典模式下, 加載Web.config配置中的HttpModules 以及 動態注冊的HttpModules。
- InitIntegratedModules():集成模式下, 加載在服務器上設定的HttpModuels和Web.config里system.webserver下的HttpModuels。
- HookupEventHandlersForApplicationAndModules:根據發生的事件,調用HttpApplication實例中相應的事件處理函數。
- 創建很多實現IExecutionStep接口的類的實例并添加到當前HttpApplication實例的_execSteps中,等待回調時執行。從這里我們可以看到HttpApplication是以異步的方式處理請求, 對請求的很多處理工作都放入了_execStep等待回調時執行。
至此,除了20多個周期事件和Handler相關的代碼我們沒有講解,其它和HttpApplication相關的并且對我們有幫助的,已經差不多清晰了。關于20多個周期事件和執行Handler方面的內容,我們下一章節再做詳細解釋。
轉載參考:
文章列表