博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
MVC之前的那点事儿系列(4):Http Pipeline详细分析(上)
阅读量:5257 次
发布时间:2019-06-14

本文共 7644 字,大约阅读时间需要 25 分钟。

文章内容

继续上一章节的内容,通过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 factoryprivate 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方法的主要功能如下:

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

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

参考资料:

同步与推荐

本文已同步至目录索引:

MVC之前的那点事儿系列文章,包括了原创,翻译,转载等各类型的文章,如果对你有用,请推荐支持一把,给大叔写作的动力。

转载于:https://www.cnblogs.com/TomXu/p/3756832.html

你可能感兴趣的文章
Zerver是一个C#开发的Nginx+PHP+Mysql+memcached+redis绿色集成开发环境
查看>>
多线程实现资源共享的问题学习与总结
查看>>
Learning-Python【26】:反射及内置方法
查看>>
torch教程[1]用numpy实现三层全连接神经网络
查看>>
java实现哈弗曼树
查看>>
转:Web 测试的创作与调试技术
查看>>
python学习笔记3-列表
查看>>
程序的静态链接,动态链接和装载 (补充)
查看>>
关于本博客说明
查看>>
线程androidAndroid ConditionVariable的用法
查看>>
转载:ASP.NET Core 在 JSON 文件中配置依赖注入
查看>>
socket初识
查看>>
磁盘测试工具
查看>>
代码变量、函数命名神奇网站
查看>>
redis cli命令
查看>>
Problem B: 占点游戏
查看>>
python常用模块之sys, os, random
查看>>
HDU 2548 A strange lift
查看>>
Linux服务器在外地,如何用eclipse连接hdfs
查看>>
react双组件传值和传参
查看>>