本来这一篇, 是要继续 Pipeline 的, 但是在 Pipeline之前, 我看到了InitModules()方法, 所以决定, 在中间穿插一篇进来. 这一篇来讲一下 IHttpModule 的加载时机, 以及怎么动态注册 HttpModules.

一. 经典模式下的 InitModules 方法

首先来看一下 InitModules() 方法, 在这个方法中, 初始化了所有的module, 其中包括了配置文件中的和想要动态注册的.

接下来, 看一下方法:

private void InitModules()
{   //注册配置文件中的HttpModuleCollection modules = RuntimeConfig.GetAppConfig().HttpModules.CreateModules();   //注册想要动态注册的HttpModuleCollection other = this.CreateDynamicModules();modules.AppendCollection(other);this._moduleCollection = modules;this.InitModulesCommon();
}

1. 从配置文件中读取

RuntimeConfig.GetAppConfig().HttpModules.CreateModules()方法, 就是去获取并解析配置文件, 提取出其中的 HttpModule, 并将它加入到集合中.

internal HttpModuleCollection CreateModules()
{HttpModuleCollection modules = new HttpModuleCollection();foreach (HttpModuleAction action in this.Modules){modules.AddModule(action.Entry.ModuleName, action.Entry.Create());}modules.AddModule("DefaultAuthentication", DefaultAuthenticationModule.CreateDefaultAuthenticationModuleWithAssert());return modules;
}

2. 动态注册

先看一下动态注册的方法吧.

private HttpModuleCollection CreateDynamicModules()
{HttpModuleCollection modules = new HttpModuleCollection();foreach (DynamicModuleRegistryEntry entry in _dynamicModuleRegistry.LockAndFetchList()){HttpModuleAction action = new HttpModuleAction(entry.Name, entry.Type);modules.AddModule(action.Entry.ModuleName, action.Entry.Create());}return modules;
}

注意到这个方法,与上个方法唯一不同的地方就是 遍历的来源不同. OK, 那在看一下那个方法里面干了些什么.

public ICollection<DynamicModuleRegistryEntry> LockAndFetchList()
{lock (this._lockObj){this._entriesReadonly = true;return this._entries;}
}

也就是说, 动态注册的HttpModule必须要加入到 _entries 变量中.

恰好, HttpApplication 中, 有一个方法, RegisterModule 可以注册module. 来看一下这个方法.

public static void RegisterModule(Type moduleType)
{if (!RuntimeConfig.GetAppConfig().HttpRuntime.AllowDynamicModuleRegistration){throw new InvalidOperationException(SR.GetString("DynamicModuleRegistrationOff"));}RegisterModuleInternal(moduleType);
}

继续看.

internal static void RegisterModuleInternal(Type moduleType)
{_dynamicModuleRegistry.Add(moduleType);
}

这里的 _dynamicModuleRegistry 变量是HttpApplication 中的一个私有字段.

private static readonly DynamicModuleRegistry _dynamicModuleRegistry;

继续看这个Add方法.

public void Add(Type moduleType)
{if (moduleType == null){throw new ArgumentNullException("moduleType");}if (!typeof(IHttpModule).IsAssignableFrom(moduleType)){throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,         SR.GetString("DynamicModuleRegistry_TypeIsNotIHttpModule"),        new object[] { moduleType }), "moduleType");}lock (this._lockObj){if (this._entriesReadonly){throw new InvalidOperationException(SR.GetString("DynamicModuleRegistry_ModulesAlreadyInitialized"));}this._entries.Add(new DynamicModuleRegistryEntry(MakeUniqueModuleName(moduleType), moduleType.AssemblyQualifiedName));}
} 

看到上面这两句标红的没有, 是不是好像在哪里见过? 其实就是上面的 LockAndFetchList() 方法中的. 但是有一个问题, 在这个方法中,  将变量 _entriesReadonly 标记为 true 了, 也就是说, 如果我们想要动态注册上 HttpModule, 那必须是在这个方法执行之前注册进去才行, 否则是会报错的.

插叙:

在集成模式之前, 先插两个例子吧.

1. Web.config实现方式

我在类库中, 建了这么一个类:

public class MyModules : IHttpModule
{public void Dispose() { }public void Init(HttpApplication context){context.BeginRequest += new EventHandler(MyBeginRequest);}void MyBeginRequest(object sender, EventArgs e){HttpApplication app = sender as HttpApplication;if (app != null){app.Response.Write("<p>经过MyModules处理过了</p>");}}
}

然后在<system.webServer>节点下面, 添加一个注册节点.

<modules><add name="MyModules" type="MyModule.MyModules"/>
</modules>

然后, 把程序运行起来, 看一下是否会报错.

实践证明, 是可以的.

2. 动态注册

一般如果我想注册一个模块进项目, 首先想到的地方, 就是 Golabl.asax文件中 Application_Start 方法了, 可是写在这个方法里面, 显然并不能满足要求.

沿用上面的例子, 然后在 Application_Start()中去注册它, 看一下是否能注册成功.

那如果我把它写到静态构造函数中去呢, 在这个类第一次加载的时候, 就注册一下试试.

static MvcApplication()
{HttpApplication.RegisterModule(typeof(MyModules));
}

果然是可以的哦.

但是这里有一个非常大的问题, 让我想不通. 从之前的代码中, 其实可以看到, Application_Start方法的调用其实是非常早的, 但是为什么不能再 Application_Start方法里面注册HttpModules, 而要做到静态构造函数里面. 好吧, 这个问题困扰了我一天, 才发现自己走了岔路. 先不表, 接着看集成模式的吧. 答案就在下面.

二、集成模式下  InitIntegratedModules 方法

private void InitIntegratedModules()
{this._moduleCollection = this.BuildIntegratedModuleCollection(_moduleConfigInfo);this.InitModulesCommon();
}

1. 先看 BuildIntegratedModuleCollection 方法

private HttpModuleCollection BuildIntegratedModuleCollection(List<ModuleConfigurationInfo> moduleList)
{HttpModuleCollection modules = new HttpModuleCollection();foreach (ModuleConfigurationInfo info in moduleList){ModulesEntry entry = new ModulesEntry(info.Name, info.Type, "type", null);modules.AddModule(entry.ModuleName, entry.Create());}return modules;
}

这里其实就是处理 HttpApplication._moduleConfigInfo 中存放的Module. 那么就产生了一个疑问, 这个HttpApplication._moduleConfigInfo在这里是直接拿出来用的, 但是好像没有任何地方对他进行赋值, 起码, 在我解析的过程中, 并没有看到在哪里对他进行了赋值. 肿么办呢?

我立马想到了, 在前面调用过 HttpApplicationFactory.GetSpecialApplicationInstance()方法来产生过一个特殊的HttpApplication对象, 是不是在那里面做了赋值操作呢? 来看一下这个方法.

private HttpApplication GetSpecialApplicationInstance(IntPtr appContext, HttpContext context)
{HttpApplication application = null;lock (this._specialFreeList){if (this._numFreeSpecialAppInstances > 0){application = (HttpApplication) this._specialFreeList.Pop();this._numFreeSpecialAppInstances--;}}if (application == null){using (new DisposableHttpContextWrapper(context)){application = (HttpApplication) HttpRuntime.CreateNonPublicInstance(this._theApplicationType);using (new ApplicationImpersonationContext()){application.InitSpecial(this._state, this._eventHandlerMethods, appContext, context);}}}return application;
}

经典模式下看的是 InitInternal 方法, 这里就来看一下 InitSpecial 方法吧.

internal void InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context)
{this._state = state;try{if (context != null){this._initContext = context;this._initContext.ApplicationInstance = this;}if (appContext != IntPtr.Zero){using (new ApplicationImpersonationContext()){HttpRuntime.CheckApplicationEnabled();}this.InitAppLevelCulture();this.RegisterEventSubscriptionsWithIIS(appContext, context, handlers);}else{this.InitAppLevelCulture();if (handlers != null){this.HookupEventHandlersForApplicationAndModules(handlers);}}if ((appContext != IntPtr.Zero) && ((this._appPostNotifications != 0) || (this._appRequestNotifications != 0))){this.RegisterIntegratedEvent(appContext, "global.asax",           this._appRequestNotifications,           this._appPostNotifications,           base.GetType().FullName, "managedHandler", false);
        }}finally{_initSpecialCompleted = true;if (this._initContext != null){this._initContext.ApplicationInstance = null;this._initContext = null;}}
}

这里似乎并不能看出什么, 那么接着来看标红的方法.

private void RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers)
{RequestNotification notification;RequestNotification notification2;this.RegisterIntegratedEvent(appContext, "AspNetFilterModule",     RequestNotification.LogRequest | RequestNotification.UpdateRequestCache,     0, string.Empty, string.Empty, true);this._moduleCollection = this.GetModuleCollection(appContext);if (handlers != null){this.HookupEventHandlersForApplicationAndModules(handlers);}HttpApplicationFactory.EnsureAppStartCalledForIntegratedMode(context, this);this._currentModuleCollectionKey = "global.asax";try{this._hideRequestResponse = true;context.HideRequestResponse = true;this._context = context;this.Init();}catch (Exception exception){this.RecordError(exception);Exception error = context.Error;if (error != null){throw error;}}finally{this._context = null;context.HideRequestResponse = false;this._hideRequestResponse = false;}this.ProcessEventSubscriptions(out notification, out notification2);this._appRequestNotifications |= notification;this._appPostNotifications |= notification2;for (int i = 0; i < this._moduleCollection.Count; i++){this._currentModuleCollectionKey = this._moduleCollection.GetKey(i);IHttpModule module = this._moduleCollection.Get(i);ModuleConfigurationInfo info = _moduleConfigInfo[i];module.Init(this);this.ProcessEventSubscriptions(out notification, out notification2);if ((notification != 0) || (notification2 != 0)){this.RegisterIntegratedEvent(appContext, info.Name, notification, notification2, info.Type, info.Precondition, false);}}this.RegisterIntegratedEvent(appContext, "ManagedPipelineHandler", RequestNotification.ExecuteRequestHandler | RequestNotification.MapRequestHandler, RequestNotification.EndRequest, string.Empty, string.Empty, false);
}

这里有个方法叫 HttpApplication.GetModuleCollection(), 来看一下

private HttpModuleCollection GetModuleCollection(IntPtr appContext)
{if (_moduleConfigInfo == null){List<ModuleConfigurationInfo> list = null;IntPtr zero = IntPtr.Zero;IntPtr bstrModuleName = IntPtr.Zero;int cchModuleName = 0;IntPtr bstrModuleType = IntPtr.Zero;int cchModuleType = 0;IntPtr bstrModulePrecondition = IntPtr.Zero;int cchModulePrecondition = 0;try{int count = 0;int num5 = UnsafeIISMethods.MgdGetModuleCollection(IntPtr.Zero, appContext, out zero, out count);if (num5 < 0){throw new HttpException(SR.GetString("Cant_Read_Native_Modules", new object[] { num5.ToString("X8", CultureInfo.InvariantCulture) }));}list = new List<ModuleConfigurationInfo>(count);for (uint i = 0; i < count; i++){num5 = UnsafeIISMethods.MgdGetNextModule(zero, ref i, out bstrModuleName, out cchModuleName, out bstrModuleType, out cchModuleType, out bstrModulePrecondition, out cchModulePrecondition);if (num5 < 0){throw new HttpException(SR.GetString("Cant_Read_Native_Modules", new object[] { num5.ToString("X8", CultureInfo.InvariantCulture) }));}string str = (cchModuleName > 0) ? StringUtil.StringFromWCharPtr(bstrModuleName, cchModuleName) : null;string str2 = (cchModuleType > 0) ? StringUtil.StringFromWCharPtr(bstrModuleType, cchModuleType) : null;string condition = (cchModulePrecondition > 0) ? StringUtil.StringFromWCharPtr(bstrModulePrecondition, cchModulePrecondition) : string.Empty;Marshal.FreeBSTR(bstrModuleName);bstrModuleName = IntPtr.Zero;cchModuleName = 0;Marshal.FreeBSTR(bstrModuleType);bstrModuleType = IntPtr.Zero;cchModuleType = 0;Marshal.FreeBSTR(bstrModulePrecondition);bstrModulePrecondition = IntPtr.Zero;cchModulePrecondition = 0;if (!string.IsNullOrEmpty(str) && !string.IsNullOrEmpty(str2)){list.Add(new ModuleConfigurationInfo(str, str2, condition));}}}finally{if (zero != IntPtr.Zero){Marshal.Release(zero);zero = IntPtr.Zero;}if (bstrModuleName != IntPtr.Zero){Marshal.FreeBSTR(bstrModuleName);bstrModuleName = IntPtr.Zero;}if (bstrModuleType != IntPtr.Zero){Marshal.FreeBSTR(bstrModuleType);bstrModuleType = IntPtr.Zero;}if (bstrModulePrecondition != IntPtr.Zero){Marshal.FreeBSTR(bstrModulePrecondition);bstrModulePrecondition = IntPtr.Zero;}}list.AddRange(this.GetConfigInfoForDynamicModules());_moduleConfigInfo = list;}return this.BuildIntegratedModuleCollection(_moduleConfigInfo);
}

好长啊, 很多都看不懂, 不过没关系, 直接看我标红的方法.

private IEnumerable<ModuleConfigurationInfo> GetConfigInfoForDynamicModules()
{return (from entry in _dynamicModuleRegistry.LockAndFetchList() 

    select new ModuleConfigurationInfo(entry.Name, entry.Type, "managedHandler"));
}

这里也出现了 LockAndFetchList() 方法, 也就是说, 在产生特殊HttpApplication对象的时候, 集成模式下, 在创建特殊对象的过程中, 就已经注册了 HttpModules了, 然后才调用的 Application_Start方法, 这也就解释了, 之前我想不通的问题.

其实这里, 主要是我在之前忽略掉了一个问题, IIS的集成模式和经典模式的运行差异性问题. 他们实现的功能其实差不多, 就是过程稍有不同.

2. InitModulesCommon 方法

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

这里就是循环遍历集合中的HttpModules, 调用他的 Init 方法.

参考:

  你必须知道的Asp.net知识

  MVC之前的那些事儿

目录已同步

转载于:https://www.cnblogs.com/elvinle/p/6269259.html

MVC源码解析 - 配置注册 / 动态注册 HttpModule相关推荐

  1. Spring MVC源码解析——HandlerMapping(处理器映射器)

    Sping MVC 源码解析--HandlerMapping处理器映射器 1. 什么是HandlerMapping 2. HandlerMapping 2.1 HandlerMapping初始化 2. ...

  2. c语言追加字符串_Redis源码解析二--简单动态字符串

    Redis 简单动态字符串 1.介绍 Redis兼容传统的C语言字符串类型,但没有直接使用C语言的传统的字符串(以'0'结尾的字符数组)表示,而是自己构建了一种名为简单动态字符串(simple dyn ...

  3. Spring4.x源码解析:JDK动态代理成生成代理对象源码

    @Component("aopTestBean") class AopTestBean implements AopTestBeanInterface{public void ao ...

  4. Spring MVC源码解析

    Spring Mvc结构解析 上图是Dispatcher Servlet的结构图,从图中可以清楚的看到Dispatcher Servlet的继承链,下面我们将基于Spring4.1.6揭开Spring ...

  5. EventBus3.0源码解析

     本文主要介绍EventBus3.0的源码 EventBus是一个Android事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递. EventBus使用简单,并将事件发布和 ...

  6. .net core 源码解析-mvc route的注册,激活,调用流程(三)

    .net core mvc route的注册,激活,调用流程 mvc的入口是route,当前请求的url匹配到合适的route之后,mvc根据route所指定的controller和action激活c ...

  7. 注册中心 Eureka 源码解析 —— 应用实例注册发现(五)之过期

    2019独角兽企业重金招聘Python工程师标准>>> 摘要: 原创出处 http://www.iocoder.cn/Eureka/instance-registry-evict/ ...

  8. 【EventBus】EventBus 源码解析 ( 注册订阅者 | 注册订阅方法详细过程 )

    文章目录 前言 一.EventBus 注册订阅者 二.注册订阅方法的具体过程 三.Subscription 类 前言 在上一篇博客 [EventBus]EventBus 源码解析 ( 注册订阅者 | ...

  9. Spring 注解面面通 之 @CrossOrigin 注册处理方法源码解析

      参照<Spring 注解面面通 之 @RequestMapping 注册处理方法源码解析>,其讲解了@RequestMapping注释的处理方法注册过程,而@CrossOrigin是基 ...

最新文章

  1. charles和Fiddler感觉哪个更好用
  2. 怎么中断一个ajax请求,ajax、fetch、axios如何中断请求?
  3. Oracle 11g安装
  4. Storm ack和fail机制再论
  5. sendMessage 与 obtainMessage (sendToTarget)比较
  6. 解决虚拟机安装64位系统“此主机支持 Intel VT-x,但 Intel VT-x 处于禁用状态”的问题...
  7. Linux网络编程:原始套接字的魔力【续】
  8. alwayson高可用组_AlwaysOn可用性组–好奇心使您的工作更轻松–第4部分
  9. 【CV】图像恢复(去噪,去模糊,超分)模型 DPDNN 论文笔记
  10. Lora如何组网?有哪些简单的Lora组网协议?
  11. 4个中英文翻译神器,一键翻译国外网站!支持超多语种
  12. 图像预训练模型的起源解说和使用示例
  13. Java---设计【学生科技竞赛管理系统】
  14. 第四章 单分支结构、二分结构与多分支结构
  15. 理解一维数组中 buf、buf[0]、buf[0]、buf 四个符号的含义
  16. 千锋教育实训day07————java
  17. 浅谈JVM(六):方法调用过程
  18. Vue非父子组件通信的几种方式
  19. 【智能车Code review】——拐点的寻找
  20. 游戏加速器的流程和具体延迟是什么?

热门文章

  1. Java里进制转换(二进制、八进制、十进制、十六进制)
  2. webstorm与Idea禁用自动保存
  3. Android开发笔记(二十七)对象序列化
  4. 小程序解码时 php 7.0以上 mcrypt拓展无法使用 旧版本的解密解决方案 新版本在另外一篇文章
  5. laravel5.5 尝试使用laravel安装器安装(失败) 最后还是用的composer。。。
  6. mac下多个php版本快速切换的方法
  7. PostgreSQL的实践一:数据类型(一)
  8. [CENTOS7] [IPTABLES] 卸载Firewall Id安装 IPTABLES及防火墙设置
  9. 吃是为了肉体,喝是为了灵魂
  10. 在Gilt使用Scala、Docker和AWS演化微服务