.Net MVC 裡面的核心檔案Global.asax執行原理分析
這個 Global.asax檔案是MVC裡面的起始檔案,它不同於其它檔案比如,M層,V層和C層。他是一個獨立的可配置的檔案。
因為.Net MVC把前後端完全分離,極度抽象的一個框架。或許導致了很多從WebForm轉過來的不適應。Global檔案就是此類。
它與MVC裡面的篩選器,控制器類工程(controllerFactory)的互動式怎麼做大的呢?
實際上在System.Web名稱空間裡面,PipelineRunTime類的InitializeApplication方法會通過HttpApplicationFactory呼叫GetPipelineApplicationInstance方法建立Global.asax檔案類。
app(Global.asax) = HttpApplicationFactory.GetPipelineApplicationInstance(appContext, context); GetPipelineApplicationInstance呼叫如下: internal static HttpApplication GetPipelineApplicationInstance(IntPtr appContext, HttpContext context) { _theApplicationFactory.EnsureInited(); return _theApplicationFactory.GetSpecialApplicationInstance(appContext, context); }
_theApplicationFactory 是一個全域性靜態類例項 private static HttpApplicationFactory _theApplicationFactory = new HttpApplicationFactory();實際上就是httpApplicationFactory類工場例項。
EnsureInited程式碼如下
private void EnsureInited() { if (!this._inited) { HttpApplicationFactory factory = this; lock (factory) { 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;
}
}
}
CompileApplication程式碼如下:
private void CompileApplication()
{
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();
}
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];
}
}
ReflectOnMethodInfoIfItLooksLikeEventHandler程式碼如下:
private bool ReflectOnMethodInfoIfItLooksLikeEventHandler(MethodInfo m)
{
if (m.ReturnType != typeof(void))
{
return false;
}
ParameterInfo[] parameters = m.GetParameters();
int length = parameters.Length;
if (length != 0)
{
if (length != 2)
{
return false;
}
if (parameters[0].ParameterType != typeof(object))
{
return false;
}
if ((parameters[1].ParameterType != typeof(EventArgs)) && !parameters[1].ParameterType.IsSubclassOf(typeof(EventArgs)))
{
return false;
}
}
string name = m.Name;
int index = name.IndexOf('_');
if ((index <= 0) || (index > (name.Length - 1)))
{
return false;
}
if (StringUtil.EqualsIgnoreCase(name, "Application_OnStart") || StringUtil.EqualsIgnoreCase(name, "Application_Start"))
{
this._onStartMethod = m;
this._onStartParamCount = parameters.Length;
}
else if (StringUtil.EqualsIgnoreCase(name, "Application_OnEnd") || StringUtil.EqualsIgnoreCase(name, "Application_End"))
{
this._onEndMethod = m;
this._onEndParamCount = parameters.Length;
}
else if (StringUtil.EqualsIgnoreCase(name, "Session_OnEnd") || StringUtil.EqualsIgnoreCase(name, "Session_End"))
{
this._sessionOnEndMethod = m;
this._sessionOnEndParamCount = parameters.Length;
}
return true;
}
ReflectOnMethodInfoIfItLooksLikeEventHandler裡面獲取到了當前Global.asax方法Application_OnStart
再來看GetPipelineApplicationInstance方法裡面呼叫的第二個方法GetSpecialApplicationInstance。它裡面會呼叫 application = (HttpApplication) HttpRuntime.CreateNonPublicInstance(this._theApplicationType);建立HtppApplication ,實際上就是Global.asax裡面的類MvcApplication。它會呼叫ProcessSpecialRequest方法反射上面得到的HttpApplication裡面的方法Application_start,最後用Invoke呼叫。
this.InvokeMethodWithAssert(method, paramCount, eventSource, eventArgs);
[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
{
object[] parameters = new object[] { eventSource, eventArgs };
method.Invoke(this, parameters);
}
}
最後進入到Application_start()裡面呼叫MVC的一些前奏,比如配置全域性篩選器,建立路由表,註冊Area等等。
值得注意的是,這一系列事件都在 進入HTTPModule和HttpHandler之前發生,所以後面等到啟用Controller呼叫ControllerFactory和篩選器遞迴鏈的時候,Application_start裡面的配置都會起作用。
還有一點需要注意的是,這個Application_start只會在第一次請求載入,後續陸續請求不會再次載入。如果需要重新載入,關掉程式,重新來過,則又會執行到Application_start方法裡面。(Java/.NET討論群:676817308)