这篇文章花了点时间,差点成烂到电脑里面,写的过程中有好几次修改,最终的这个版本也不是很满意,东西说的不够细,还需要认真的去看下源码才能有所体会,先这样吧,后面有时间把细节慢慢的再修改。顺便对于开发的学习,个人是觉得源码的阅读是最快的提高方式,当然阅读不是走马观花,应该多次阅读。

上次说到获得HttpApplication对象的创建,创建完成后调用InitInternal方法,这个方法任务比较多,也比较长,这里就不贴全码了,一个一个过程的去说:

初始化HttpModule

对于HttpModule的认识,首先应该看下HttpModule的使用情况,下面通过一个简单的例子展示:

httpModule使用实例

  1. 新建一个项目,添加一个webform的窗体default.aspx,使用IIS添加到网站,应用程序池使用集成模式。
  2. 添加一个MyModule.cs,继承自IHttpModule。
  3. 在IHttpMoudule中有两个方法,在MyModule中必须要实现:

    public void Init(HttpApplication context)
    {
       throw new System.NotImplementedException();
    }
    
    public void Dispose()
    {
       throw new System.NotImplementedException();
    }
  4. 在Init方法中,有一个HttpApplication类型的对象context,这里可以对其中的响应的内容进行更改,修改如下:

    public void Init(HttpApplication context)
    {
       context.EndRequest += Context_EndRequest;
    }
    
    private void Context_EndRequest(object sender, System.EventArgs e)
    {
      var context = (HttpApplication) sender;
      context.Response.Write("<h1>Hello MyModule</h1>");
    }
  5. 添加web.config文件如下(在 system.webServer下 modules节点下面):

    <add name="MyModule" type="Application.MyModule,Application"/>

    运行结果:

  6. 在上面的例子中,使用的是集成模式,当改成经典模式的时候,module又不起作用了。对于经典模式的配置文件与集成模式不同。经典模式的配置文件如下:

    <httpModules>
       <add name="MyModule" type="IISIntegratedPipeline.MyModule,IISIntegratedPipeline"/>
    </httpModules>
  7. 对于 module的使用,有了一个简单的认识,在asp.net中module是一个灵活的配置,可以对请求进行自定义的处理,对于Asp.net如何处理的,在下面详细解说。

asp.net中HttpModule的处理

  1. 结合上面例子,HttpModule在Asp.net中有重要的作用,可以HttpApplication的事件进行订阅,也可以修改对应的响应的内容

  2. 对于HttpModule的初始化,asp.Net中会根据当前应用程序池的类型进行初始化,核心代码如下:

    if (HttpRuntime.UseIntegratedPipeline) {
        try {
        context.HideRequestResponse = true;
        _hideRequestResponse = true;
        InitIntegratedModules();
    }
    finally {
        context.HideRequestResponse = false;
        _hideRequestResponse = false;
       }
    }
    else {
        InitModules();
     }
  3. 对于Module的理解,需要根据应用程序池的模式来处理(经典和集成)。
  4. 对于集成模式,获得所有Modules的方法是调用非托管的方法的进行获得,具体获得的代码如下:

    • InitIntegratedModules的方法

      private void InitIntegratedModules()
      {
          _moduleCollection = BuildIntegratedModuleCollection(_moduleConfigInfo);
          InitModulesCommon();
      }
    • _moduleConfigInfo 的来源
      这个_moduleConfigInfo的来源,还需要追到上篇 HttpApplication中三个方法的调用(EnsureAppStartCalled 第二个方法的调用)具体调用步骤如下:

         [DllImport(_IIS_NATIVE_DLL)]
            internal static extern int MgdGetModuleCollection(
            IntPtr pConfigSystem,
            IntPtr appContext,
            out IntPtr pModuleCollection,
            out int count);
  5. 对于经典模式获得Modules简单的多,直接获得是调用配置文件

    private void InitModules()
    {
        HttpModulesSection pconfig = RuntimeConfig.GetAppConfig().HttpModules;
    
        // get the static list, then add the dynamic members
        HttpModuleCollection moduleCollection = pconfig.CreateModules();
        HttpModuleCollection dynamicModules = CreateDynamicModules();
    
        moduleCollection.AppendCollection(dynamicModules);
        _moduleCollection = moduleCollection; // don't assign until all ops have succeeded
    
        InitModulesCommon();
    }
  6. 最终会调用 InitModulesCommon方法,循环调用Modules中的方法

     private void InitModulesCommon()
     {
            int n = _moduleCollection.Count;
    
            ; i < n; i++) {
    
            _currentModuleCollectionKey = _moduleCollection.GetKey(i);
             _moduleCollection[i].Init(this);
            }
    
            _currentModuleCollectionKey = null;
            InitAppLevelCulture();
     }

Global内的方法调用

对于Global方法的调用,是调用HookupEventHandlersForApplicationAndModules(handlers);方法,这里的Handlers的收集和创建来源于上篇讲HttpAplication的三个方法调用的第一个方法。具体的看下上次的代码,这里不多叙述。对于方法的handlers的调用的核心代码如下,其实也是一个循环加上判断:

 ; i < handlers.Length; i++) {
        MethodInfo appMethod = handlers[i];
        String appMethodName = appMethod.Name;
        int namePosIndex = appMethodName.IndexOf('_');
        String targetName = appMethodName.Substring(, namePosIndex);

        ...
        ParameterInfo[] addMethodParams = addMethod.GetParameters();

        )
        continue;
        Delegate handlerDelegate = null;

        ParameterInfo[] appMethodParams = appMethod.GetParameters();

        ...
        try {
            addMethod.Invoke(target, ]{handlerDelegate});
        }
        catch {
            if (HttpRuntime.UseIntegratedPipeline) {
                throw;
            }
        }

    if (eventName != null) {
        if (_pipelineEventMasks.ContainsKey(eventName)) {
            if (!StringUtil.StringStartsWith(eventName, "Post")) {
            _appRequestNotifications |= _pipelineEventMasks[eventName];
            }
            else {
                _appPostNotifications |= _pipelineEventMasks[eventName];
            }
        }
    }
}

根据应用程序池的类型创建不同的_stepManager

这里很简单,直接看代码:

// Construct the execution steps array
if (HttpRuntime.UseIntegratedPipeline) {
  _stepManager = new PipelineStepManager(this);
}
else {
 _stepManager = new ApplicationStepManager(this);
}

执行BuildStep

BuildStep与ResumeStep是Asp.net的核心运行环节。同样,在经典模式与集成模式下原理和过程也有所不一样。

集成模式

  1. 下面先讨论集成模式下是如何进行的。

    internal override void BuildSteps(WaitCallback stepCallback)
        {
            HttpApplication app = _application;
    
            IExecutionStep materializeStep = new MaterializeHandlerExecutionStep(app);
    
            app.AddEventMapping(
            HttpApplication.IMPLICIT_HANDLER,
            RequestNotification.MapRequestHandler,
            false, materializeStep);
    
            app.AddEventMapping(
            HttpApplication.IMPLICIT_HANDLER,
            RequestNotification.ExecuteRequestHandler,
            false, app.CreateImplicitAsyncPreloadExecutionStep());
    
            IExecutionStep handlerStep = new CallHandlerExecutionStep(app);
    
            app.AddEventMapping(
            HttpApplication.IMPLICIT_HANDLER,
            RequestNotification.ExecuteRequestHandler,
            false, handlerStep);
    
            IExecutionStep webSocketsStep = new TransitionToWebSocketsExecutionStep(app);
    
            app.AddEventMapping(
            HttpApplication.IMPLICIT_HANDLER,
            RequestNotification.EndRequest,
            true /* isPostNotification */, webSocketsStep);
    
            IExecutionStep filterStep = new CallFilterExecutionStep(app);
            app.AddEventMapping(
            HttpApplication.IMPLICIT_FILTER_MODULE,
            RequestNotification.UpdateRequestCache,
            false, filterStep);
    
            app.AddEventMapping(
            HttpApplication.IMPLICIT_FILTER_MODULE,
            RequestNotification.LogRequest,
            false, filterStep);
    
            _resumeStepsWaitCallback = stepCallback;
        }
  2. 上面的代码是核心是AddEventMapping方法,把相关的步骤添加到一个PipelineModuleStepContainer.

    private void AddEventMapping(string moduleName,RequestNotification requestNotification,bool isPostNotification, IExecutionStep step)
    {
    
        ThrowIfEventBindingDisallowed();
        if (!IsContainerInitalizationAllowed) {
            return;
        }
        PipelineModuleStepContainer container = GetModuleContainer(moduleName);
        if (container != null) {
            container.AddEvent(requestNotification, isPostNotification, step);
        }
    }

经典模式

1.看下代码:

internal override void BuildSteps(WaitCallback stepCallback ) {
        ArrayList steps = new ArrayList();
        HttpApplication app = _application;

        bool urlMappingsEnabled = false;
        UrlMappingsSection urlMappings = RuntimeConfig.GetConfig().UrlMappings;
        urlMappingsEnabled = urlMappings.IsEnabled && ( urlMappings.UrlMappings.Count >  );

        steps.Add(new ValidateRequestExecutionStep(app));
        steps.Add(new ValidatePathExecutionStep(app));

        if (urlMappingsEnabled)
        steps.Add(new UrlMappingsExecutionStep(app)); // url mappings

        app.CreateEventExecutionSteps(HttpApplication.EventBeginRequest, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventAuthenticateRequest, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventDefaultAuthentication, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventPostAuthenticateRequest, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventAuthorizeRequest, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventPostAuthorizeRequest, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventResolveRequestCache, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventPostResolveRequestCache, steps);
        steps.Add(new MapHandlerExecutionStep(app)); // map handler
        app.CreateEventExecutionSteps(HttpApplication.EventPostMapRequestHandler, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventAcquireRequestState, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventPostAcquireRequestState, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventPreRequestHandlerExecute, steps);
        steps.Add(app.CreateImplicitAsyncPreloadExecutionStep()); // implict async preload step
        steps.Add(new CallHandlerExecutionStep(app)); // execute handler
        app.CreateEventExecutionSteps(HttpApplication.EventPostRequestHandlerExecute, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventReleaseRequestState, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventPostReleaseRequestState, steps);
        steps.Add(new CallFilterExecutionStep(app)); // filtering
        app.CreateEventExecutionSteps(HttpApplication.EventUpdateRequestCache, steps);
        app.CreateEventExecutionSteps(HttpApplication.EventPostUpdateRequestCache, steps);
        _endRequestStepIndex = steps.Count;
        app.CreateEventExecutionSteps(HttpApplication.EventEndRequest, steps);
        steps.Add(new NoopExecutionStep()); // the last is always there

        _execSteps = new IExecutionStep[steps.Count];
        steps.CopyTo(_execSteps);

        _resumeStepsWaitCallback = stepCallback;
    }

对于上面的代码,可以看出都调用了 CreateEventExecutionSteps 方法,这个方法的详细如下 :

 private void CreateEventExecutionSteps(Object eventIndex, ArrayList steps) {
            // async
            AsyncAppEventHandler asyncHandler = AsyncEvents[eventIndex];
            if (asyncHandler != null) {
                asyncHandler.CreateExecutionSteps(this, steps);
            }
            EventHandler handler = (EventHandler)Events[eventIndex];

            if (handler != null) {
                Delegate[] handlers = handler.GetInvocationList();
                ; i < handlers.Length; i++) {
                steps.Add(new SyncEventExecutionStep(this, (EventHandler)handlers[i]));
            }
        }
    }

可以看出, CreateEventExecutionSteps是把注册的步骤都转换成了SyncEventExecutionStep,最终会被按顺序进行调用。

执行BeginProcessRequest

HttpApplication在完成BuildSteps的时候,把生成的App经过层层返回到HttpRuntime,前面几篇文章提到,在HttpRuntime里面有对app的类型进行判断,如果是IHttpAsyncHandler直接调用BeginProcessRequest方法,具体的代码如下:

 if (app is IHttpAsyncHandler) {
            IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler)app;
            context.AsyncAppHandler = asyncHandler;
            asyncHandler.BeginProcessRequest(context, _handlerCompletionCallback, context);
        }
        else {
            // synchronous handler
            app.ProcessRequest(context);
            FinishRequest(context.WorkerRequest, context, null);
        }

BeginProcessRequest 方法:

IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) {
        HttpAsyncResult result;

        _context = context;
        _context.ApplicationInstance = this;

        _stepManager.InitRequest();

        _context.Root();

        result = new HttpAsyncResult(cb, extraData);

        AsyncResult = result;

        if (_context.TraceIsEnabled)
        HttpRuntime.Profile.StartRequest(_context);

        ResumeSteps(null);
        return result;
    }

其中最核心的方法是ResumeSteps方法,具体如下:

internal override void ResumeSteps(Exception error)
{

    for (; ; ) {

        // ...

        IExecutionStep step = _application.CurrentModuleContainer.GetNextEvent(context.CurrentNotification, context.IsPostNotification,context.CurrentModuleEventIndex);

        context.SyncContext.Enable();

        stepCompletedSynchronously = false;

        //*******
        error = _application.ExecuteStep(step, ref stepCompletedSynchronously);
        //*********
        ...

        if (!stepCompletedSynchronously) {
            _application.AcquireNotifcationContextLock(ref locked);
            context.NotificationContext.PendingAsyncCompletion = true;
            break;
        }
        else {
            context.Response.SyncStatusIntegrated();
        }
    }
}

BeginProcessRequest 方法:

IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) {
    HttpAsyncResult result;

    _context = context;
    _context.ApplicationInstance = this;

    _stepManager.InitRequest();

    _context.Root();

    result = new HttpAsyncResult(cb, extraData);

    AsyncResult = result;

    if (_context.TraceIsEnabled)
    HttpRuntime.Profile.StartRequest(_context);

    ResumeSteps(null);
    return result;
}

其中最核心的方法是ResumeSteps方法,具体如下:

internal override void ResumeSteps(Exception error)
    {
        ...
        for (; ; ) {

        // ...

        IExecutionStep step = _application.CurrentModuleContainer.GetNextEvent(context.CurrentNotification, context.IsPostNotification,context.CurrentModuleEventIndex);

        context.SyncContext.Enable();

        stepCompletedSynchronously = false;

        //*******
        error = _application.ExecuteStep(step, ref stepCompletedSynchronously);
        //*********
        ...

        if (!stepCompletedSynchronously) {
            _application.AcquireNotifcationContextLock(ref locked);
            context.NotificationContext.PendingAsyncCompletion = true;
            break;
        }
        else {
            context.Response.SyncStatusIntegrated();
            }
        }
    }

对于上面的内容总结原理为:

写于 2017.03.21

第37篇 Asp.Net源码解析(二)--详解HttpApplication的更多相关文章

  1. 第36篇 Asp.Net源码解析(一)

    上面两篇文章说了http协议和IIS处理,这次说下当IIS把请求交给Asp.net后的过程. AppManagerAppDomainFactory 当IIS把请求交给asp.net时候,如果AppDo ...

  2. JQuery 源码解析 · extend()详解

    前言:最近想重写一个dropdown插件,于是想到了使用jquey实现插件,于是重温了一波$.extend()的知识,然后总结了这篇笔记 正文: $.extend(src)  jQuery.exten ...

  3. RxJava2源码解析(二)

    title: RxJava2源码解析(二) categories: 源码解析 tags: 源码解析 rxJava2 前言 本篇主要解析RxJava的线程切换的原理实现 subscribeOn 首先, ...

  4. Sentinel源码解析二(Slot总览)

    写在前面 本文继续来分析Sentinel的源码,上篇文章对Sentinel的调用过程做了深入分析,主要涉及到了两个概念:插槽链和Node节点.那么接下来我们就根据插槽链的调用关系来依次分析每个插槽(s ...

  5. Mybatis源码解析(二) —— 加载 Configuration

    Mybatis源码解析(二) -- 加载 Configuration    正如上文所看到的 Configuration 对象保存了所有Mybatis的配置信息,也就是说mybatis-config. ...

  6. 【Java】HashMap源码分析——常用方法详解

    上一篇介绍了HashMap的基本概念,这一篇着重介绍HasHMap中的一些常用方法:put()get()**resize()** 首先介绍resize()这个方法,在我看来这是HashMap中一个非常 ...

  7. 【转】ANDROID自定义视图——onMeasure,MeasureSpec源码 流程 思路详解

    原文地址:http://blog.csdn.net/a396901990/article/details/36475213 简介: 在自定义view的时候,其实很简单,只需要知道3步骤: 1.测量—— ...

  8. ANDROID自定义视图——onMeasure,MeasureSpec源码 流程 思路详解

    简介: 在自定义view的时候,其实很简单,只需要知道3步骤: 1.测量--onMeasure():决定View的大小 2.布局--onLayout():决定View在ViewGroup中的位置 3. ...

  9. React源码 commit阶段详解

    转: React源码 commit阶段详解 点击进入React源码调试仓库. 当render阶段完成后,意味着在内存中构建的workInProgress树所有更新工作已经完成,这包括树中fiber节点 ...

随机推荐

  1. Fourier分析基础(一)——Fourier级数

    前言 傅立叶分析的作用是把一个函数变成一堆三角函数的和的形式,也就是分解.首先引入的是傅立叶级数,Fourier级数的作用是把函数变为可数无限个三角函数的和,而且这些三角函数的频率都是某个基频的整数倍 ...

  2. 蓝桥网试题 java 基础练习 01字串

    ---------------------------------------------------------------------- 还括以 0.0 --------------------- ...

  3. HDU 3782 xxx定律

    xxx定律 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submi ...

  4. css遇到的那些坑——浏览器默认样式设置

    今天自己写css样式,其中用到了<ul>标签,设置了一系列效果后运行,发现位置与设置有出入.chrome上打开检查项,发现<ul>标签的styles底部多了以下一段: ul,  ...

  5. 众人拾柴火焰高之Tomcat集群

    一人拾柴火不旺,众人拾柴火焰高.Tomcat服务器也是一样,一台服务器再强大能承受的访问也是有限的.要提供高并发.高可用的服务,就必须横向扩展,多台Tomcat组成一个集群,根据实际的访问量动态增减服 ...

  6. PHP面向对象——GD库实现图片水印和缩略图

    今天的实现目标就是使用GD库完成对图片加水印和图 片缩略图两个功能 动身前逻辑准备 属性: 路径 功能: 构造方法 生成水印的方法 获取 图片信息 获取位置信息(123 456 789) 创建图片资源 ...

  7. 安卓作为udp服务器,PC作为客户端,仅监听

    安卓客户端作为udp服务器,监听其他客户端的数据,测试已成功 本次实验所用数据: 安卓作为服务器: 端口:8888            IP:192.168.1.104 电脑作为客户端: 端口:50 ...

  8. 简单了解Hibernate

    orm 对象 object 关系relational映射 mppingorm对象关系映射hibernate 框架是什么?很简单 持久化框架  他轻松的封装了jdbc那些繁琐的操作什么是持久化?持久化就 ...

  9. 【转】Spring源码编译

    原文地址: http://www.flyoung.me/2013/08/02/springcodecompile/ 参考资料: https://github.com/spring-projects/s ...

  10. MySQL备份说明

    第一次发布博客,发现目录居然不会生成,后续慢慢熟悉博客园的设置.回正文--- 1 使用规范 1.1 实例级备份恢复 使用innobackupex,在业务空闲期执行,考虑到IO影响及 FLUSH TAB ...