前言:
附上Asp.net
執行請求流程圖.
在前一篇我們說到HttpRunTime
會透過GetApplicationInstance
來取得一個IHttpHandler
對象.
今天跟著原始碼來了解到底回傳一個什麼IHttpHandler
物件給HttpRunTime
使用.
查看原始碼好站 Reference Source
HttpApplication物件
HttpApplication是整個ASP.NET
基礎的核心。一個HttpApplication物件在某個時刻只能處理一個請求,只有完成對某個請求處理後,該HttpApplication才能用於後續的請求的處理。
所以ASP.NET
利用物件程序池機制來建立或者取得HttpApplication物件。具體來講,當第一個Http
請求抵達的時候,ASP.NET
會一次建立多個HttpApplication物件,並將其置於池中,選擇其中一個物件來處理該請求。
而如果程序池中沒有HttpApplication
物件,Asp.net
會建立新的HttpApplication
物件處理請求
HttpApplication
物件處理Http
請求整個生命週期是一個相對複雜的過程,在該過程的不同階段會觸發相應的事件。我們可以註冊相應的事件(如同上一篇介紹事件表)
下圖就是模擬HttpApplication
的ObjectPool
樣子
取得使用 HttpApplication物件 (GetApplicationInstance)
讓我們看看GetApplicationInstan
方法做了什麼事情.
private static HttpApplicationFactory _theApplicationFactory = new HttpApplicationFactory();
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);
}
_theApplicationFactory
是一個靜態物件
_theApplicationFactory
呼叫三個方法EnsureInited
,EnsureAppStartCalled
,GetNormalApplicationInstance
,讓我們一一來解析做了些什麼事情吧
HttpApplicationFactory 初始化 (EnsureInited方法)
通過查找Init方法的代碼以及其中2行如下代碼裡的細節,我們可以得知,這2行代碼主要是從global.asax獲取內容,然後進行編譯。
HttpApplicationFactory.EnsureInited()
方法檢查HttpApplicationFactory
是否已經被初始化,如果沒有就呼叫HttpApplicationFactory.Init()
進行初始化。
在Init()
中,先獲取網站下global.asax
文件完整路徑(透過GetApplicationFile
方法),最後呼叫CompileApplication()
方法對global.asax
進行編譯.
在EnsureInited方法
private void EnsureInited() {
if (!_inited) {
lock (this) {
if (!_inited) {
Init();
_inited = true;
}
}
}
}
private void CompileApplication() {
// Get the Application Type and AppState from the global file
_theApplicationType = BuildManager.GetGlobalAsaxType();
BuildResultCompiledGlobalAsaxType result = BuildManager.GetGlobalAsaxBuildResult();
if (result != null) {
if (result.HasAppOrSessionObjects) {
GetAppStateByParsingGlobalAsax();
}
_fileDependencies = result.VirtualPathDependencies;
}
if (_state == null) {
_state = new HttpApplicationState();
}
ReflectOnApplicationType();
}
ReflectOnApplicationType
方法取得目前特別事件方法,並添加到相對應的MethodInfo
成員上
會透過以下三類方法名稱去取方法資訊
Application_OnStart
orApplication_Start
Application_OnEnd
orApplication_End
Session_OnEnd
orSession_End
EnsureAppStartCalled
去呼叫Application_OnStart
方法
private void ReflectOnApplicationType() {
ArrayList handlers = new ArrayList();
MethodInfo[] methods;
Debug.Trace("PipelineRuntime", "ReflectOnApplicationType");
// get this class methods
methods = _theApplicationType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
foreach (MethodInfo m in methods) {
if (ReflectOnMethodInfoIfItLooksLikeEventHandler(m))
handlers.Add(m);
}
// get base class private methods (GetMethods would not return those)
Type baseType = _theApplicationType.BaseType;
if (baseType != null && baseType != typeof(HttpApplication)) {
methods = baseType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
foreach (MethodInfo m in methods) {
if (m.IsPrivate && ReflectOnMethodInfoIfItLooksLikeEventHandler(m))
handlers.Add(m);
}
}
// remember as an array
_eventHandlerMethods = new MethodInfo[handlers.Count];
for (int i = 0; i < _eventHandlerMethods.Length; i++)
_eventHandlerMethods[i] = (MethodInfo)handlers[i];
}
Application_Start方法為什麼只會呼叫一次? (EnsureAppStartCalled)
HttpApplicationFactory.EnsureAppStartCalled
方法建立一個HttpApplication
物件並觸發Application_OnStart
事件(執行Global.asax
中的Application_Start(object sender, EventArgs e)
)
在處理完事件Application_OnStart
後HttpApplication
物件會立即被回收掉,因為系統初始化只需要一次
GetSpecialApplicationInstance
裡會對IIS7
做一些特殊的事情這裡就不多提
private void EnsureAppStartCalled(HttpContext context) {
if (!_appOnStartCalled) {
lock (this) {
if (!_appOnStartCalled) {
using (new DisposableHttpContextWrapper(context)) {
WebBaseEvent.RaiseSystemEvent(this, WebEventCodes.ApplicationStart);
FireApplicationOnStart(context);
}
_appOnStartCalled = true;
}
}
}
}
private void FireApplicationOnStart(HttpContext context) {
if (_onStartMethod != null) {
HttpApplication app = GetSpecialApplicationInstance();
app.ProcessSpecialRequest(
context,
_onStartMethod,
_onStartParamCount,
this,
EventArgs.Empty,
null);
RecycleSpecialApplicationInstance(app);
}
}
Application_OnStart
呼叫RecycleSpecialApplicationInstance
回收HttpApplication
物件返回一個 HttpApplication 物件 (GetNormalApplicationInstance)
方法中主要做.
- 判斷
_freeList
集合中是否有可用HttpApplication
物件(物件程序池中),如果沒有就利用HttpRuntime.CreateNonPublicInstance(_theApplicationType)
透過反射建立一個新的HttpApplication
返回(呼叫完IHttpHandler.ProcessRequst
方法後會將這個物件存入_freeList
中),最後將
private HttpApplication GetNormalApplicationInstance(HttpContext context) {
HttpApplication app = null;
if (!_freeList.TryTake(out app)) {
// If ran out of instances, create a new one
app = (HttpApplication)HttpRuntime.CreateNonPublicInstance(_theApplicationType);
using (new ApplicationImpersonationContext()) {
app.InitInternal(context, _state, _eventHandlerMethods);
}
}
if (AppSettings.UseTaskFriendlySynchronizationContext) {
// When this HttpApplication instance is no longer in use, recycle it.
app.ApplicationInstanceConsumersCounter = new CountdownTask(1); // representing required call to HttpApplication.ReleaseAppInstance
app.ApplicationInstanceConsumersCounter.Task.ContinueWith((_, o) => RecycleApplicationInstance((HttpApplication)o), app, TaskContinuationOptions.ExecuteSynchronously);
}
return app;
}
所以最終我們是返回一個HttpApplication
物件來使用.
小結
今天我們學到
IHttpHandler GetApplicationInstance(HttpContext context)
其實是返回一個HttpApplication
物件.- 在
EnsureAppStartCalled
方法中呼叫FireApplicationOnStart
方法動態建立一個HttpApplication
物件,呼叫完Application_OnStart
事件就回收掉並使用一個flag
布林值代表已經呼叫過. - 這個工廠會有一個
_freeList
集合來存取之前用過的HttpApplication
物件,如果集合中沒有適合的HttpApplication
物件就會使用反射返回一個新的HttpApplication
並將他初始化. - 所以
HttpRuntime
呼叫的是HttpApplication
物件的ProcessRequest
方法
下篇會跟大家介紹HttpApplication
類別成員詳細資訊
如果本文對您幫助很大,可街口支付斗內鼓勵石頭^^