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 = ; 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 :
goto Label_0089; case :
if (!(parameters[].ParameterType != typeof(object)))
{
if ((parameters[].ParameterType != typeof(EventArgs)) && !parameters[].ParameterType.IsSubclassOf(typeof(EventArgs)))
{
return false;
}
goto Label_0089;
}
return false;
}
}
return false;
Label_0089:
str = m.Name;
int index = str.IndexOf('_');
if ((index <= ) || (index > (str.Length - )))
{
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 == )
{
method.Invoke(this, new object[]);
}
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 > )
{
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();
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方法的主要功能如下:

  1. InitModules():经典模式下, 加载Web.config配置中的HttpModules 以及 动态注册的HttpModules。
  2. InitIntegratedModules():集成模式下, 加载在服务器上设定的HttpModuels和Web.config里system.webserver下的HttpModuels。
  3. HookupEventHandlersForApplicationAndModules:根据发生的事件,调用HttpApplication实例中相应的事件处理函数。
  4. 创建很多实现IExecutionStep接口的类的实例并添加到当前HttpApplication实例的_execSteps中,等待回调时执行。从这里我们可以看到HttpApplication是以异步的方式处理请求, 对请求的很多处理工作都放入了_execStep等待回调时执行。

至此,除了20多个周期事件和Handler相关的代码我们没有讲解,其它和HttpApplication相关的并且对我们有帮助的,已经差不多清晰了。关于20多个周期事件和执行Handler方面的内容,我们下一章节再做详细解释。

转载参考:

   MVC之前的那点事儿

目录已同步

MVC源码解析 - Http Pipeline 解析(上)的更多相关文章

  1. jQuery 2.0.3 源码分析Sizzle引擎解析原理

    jQuery 2.0.3 源码分析Sizzle引擎 - 解析原理 声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 先来回答博友的提问: 如何解析 div > p + ...

  2. MyBatis 源码分析 - 映射文件解析过程

    1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...

  3. FFmpeg的HEVC解码器源码简单分析:解析器(Parser)部分

    ===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...

  4. springMVC源码分析--RequestParamMethodArgumentResolver参数解析器(三)

    之前两篇博客springMVC源码分析--HandlerMethodArgumentResolver参数解析器(一)和springMVC源码解析--HandlerMethodArgumentResol ...

  5. spring mvc源码-》MultipartReques类-》主要是对文件上传进行的处理,在上传文件时,编码格式为enctype="multipart/form-data"格式,以二进制形式提交数据,提交方式为post方式。

    spring mvc源码->MultipartReques类-> MultipartReques类主要是对文件上传进行的处理,在上传文件时,编码格式为enctype="multi ...

  6. MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)

    前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...

  7. MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)

    前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...

  8. MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)

    前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...

  9. MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)

    前言:最近一段时间在学习MVC源码,说实话,研读源码真是一个痛苦的过程,好多晦涩的语法搞得人晕晕乎乎.这两天算是理解了一小部分,这里先记录下来,也给需要的园友一个参考,奈何博主技术有限,如有理解不妥之 ...

  10. [转]MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)

    本文转自:http://www.cnblogs.com/landeanfen/p/5989092.html 阅读目录 一.MVC原理解析 1.MVC原理 二.HttpHandler 1.HttpHan ...

随机推荐

  1. 通过 C# 代码操作 Google 日历

    原文:通过 C# 代码操作 Google 日历 本文主题 借助 Google .NET APIs Client Library,通过 C# 代码在 Google 日历中创建会议邀请. 本文背景 最近, ...

  2. Grub启动配置文件

    和许多其他linux发行版一样,Fedora使用Grub作为32位和64位X86系统的启动加载器(bootloader).grub的配置文件主要是/boot/grub/grub.conf,而/boot ...

  3. Android学习路径——Android的四个组成部分activity(一)

    一.什么是Activity? Activity简单的说就是一个接口.我们是Android手机上看到的每个界面就是一个activity. 二.Activity的创建 1.定义一个类继承activity, ...

  4. javascript继承之借用构造函数与原型

    javascript继承之借用构造函数与原型 在js中,关于继承只有利用构造函数和原型链两种来现实.以前所见到的种种方法与模式,只不过是变种罢了. 借用构造函数 1 2 3 4 5 6 7 8 9 1 ...

  5. SAX解析xml浅析

    SAX解析XML文件采用事件驱动的方式进行,也就是说,SAX是逐行扫描文件,遇到符合条件的设定条件后就会触发特定的事件,回调你写好的事件处理程序.使用SAX的优势在于其解析速度较快,占用内存较少(相对 ...

  6. linux不同环境下c/c++程序移植方法

    这边之前的大多数项目都用的java,而自己用的c++,等到快要上线的时候才发现线上机器的gcc和libc的版本都巨低,跟自己测试开发用的环境不兼容,编译出的c++可执行文件没法运行.解决c++程序的移 ...

  7. IOS学习之路二十四(custom 漂亮的UIColor)

    下面简单列举一下漂亮的和颜色,大家也可以自己依次试一试选出自己喜欢的. 转载请注明 本文转自:http://blog.csdn.net/wildcatlele/article/details/1235 ...

  8. javascript的关键所在---作用域链

    javascript的关键所在---作用域链 javascript里的作用域是理解javascript语言的关键所在,正确使用作用域原理才能写出高效的javascript代码,很多javascript ...

  9. 使用Guava进行函数式编程

    本文翻译自Getting Started with Google Guava这本书,如有翻译不足的地方请指出. 在这一章,我们开始注意到使用Guava进行编写代码会更加简单.我们将看看如何使用Guav ...

  10. myeclipse 配置 resin-pro-4.0.34

    热部署: 在 resin.xml 文件下 增加 <host id="" root-directory="."> <!-- webapps ca ...