MVC之前的那点事儿系列(4):Http Pipeline详细分析(上)
文章内容
继续上一章节的内容,通过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 factory
private 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 > ) {
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 >= , "_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方法的主要功能如下:
- InitModules():根据Web.Config的设置,加载相应的HttpModules。
- InitIntegratedModules():会加载IIS7集成模式下在服务器上设定的HttpModuels和Web.config里system.webserver下的HttpModuels。
- HookupEventHandlersForAppplicationAndModules:根据发生的事件,调用HttpApplication实例中相应的事件处理函数。
- 创建很多实现IExecutionStep接口的类的实例并添加到当前HttpApplication实例的_execSteps中,等待回调时执行。从这里我们可以看到HttpApplication是以异步的方式处理请求, 对请求的很多处理工作都放入了_execStep等待回调时执行。
至此,除了20多个周期事件和Handler相关的代码我们没有讲解,其它和HttpApplication相关的并且对我们有帮助的,已经差不多清晰了。关于20多个周期事件和执行Handler方面的内容,我们下一章节再做详细解释。
参考资料:
http://msdn.microsoft.com/en-us/magazine/cc188942.aspx
http://msdn.microsoft.com/en-us/library/bb470252.aspx
http://www.cnblogs.com/zhaoyang/archive/2011/11/16/2251200.html
同步与推荐
本文已同步至目录索引:MVC之前的那点事儿系列
MVC之前的那点事儿系列文章,包括了原创,翻译,转载等各类型的文章,如果对你有用,请推荐支持一把,给大叔写作的动力。
MVC之前的那点事儿系列(4):Http Pipeline详细分析(上)的更多相关文章
- MVC之前的那点事儿系列(10):MVC为什么不再需要注册通配符(*.*)了?
文章内容 很多教程里都提到了,在部署MVC程序的时候要配置通配符映射(或者是*.mvc)到aspnet_ISPAI.dll上,在.NET4.0之前确实应该这么多,但是.NET4.0之后已经不要再费事了 ...
- MVC之前的那点事儿系列(8):UrlRouting的理解
文章内容 根据对Http Runtime和Http Pipeline的分析,我们知道一个ASP.NET应用程序可以有多个HttpModuel,但是只能有一个HttpHandler,并且通过这个Http ...
- MVC之前的那点事儿系列(6):动态注册HttpModule
文章内容 通过前面的章节,我们知道HttpApplication在初始化的时候会初始化所有配置文件里注册的HttpModules,那么有一个疑问,能否初始化之前动态加载HttpModule,而不是只从 ...
- MVC之前的那点事儿系列(9):MVC如何在Pipeline中接管请求的?
文章内容 上个章节我们讲到了,可以在HttpModules初始化之前动态添加Route的方式来自定义自己的HttpHandler,最终接管请求的,那MVC是这么实现的么?本章节我们就来分析一下相关的M ...
- MVC之前的那点事儿系列(7):WebActivator的实现原理详解
文章内容 上篇文章,我们分析如何动态注册HttpModule的实现,本篇我们来分析一下通过上篇代码原理实现的WebActivator类库,WebActivator提供了3种功能,允许我们分别在Http ...
- MVC之前的那点事儿系列(5):Http Pipeline详细分析(下)
文章内容 接上面的章节,我们这篇要讲解的是Pipeline是执行的各种事件,我们知道,在自定义的HttpModule的Init方法里,我们可以添加自己的事件,比如如下代码: public class ...
- MVC之前的那点事儿系列(3):HttpRuntime详解分析(下)
文章内容 话说,经过各种各样复杂的我们不知道的内部处理,非托管代码正式开始调用ISPAIRuntime的ProcessRequest方法了(ISPAIRuntime继承了IISPAIRuntime接口 ...
- MVC之前的那点事儿系列(2):HttpRuntime详解分析(上)
文章内容 从上章文章都知道,asp.net是运行在HttpRuntime里的,但是从CLR如何进入HttpRuntime的,可能大家都不太清晰.本章节就是通过深入分析.Net4的源码来展示其中的重要步 ...
- MVC之前的那点事儿系列(1):进入CLR
MVC之前的那点事儿系列,是笔者在2012年初阅读MVC3源码的时候整理的,主要讲述的是从HTTP请求道进入MVCHandler之前的内容,包括了原创,翻译,转载,整理等各类型文章,当然也参考了博客园 ...
随机推荐
- Java处理Radius access-challenge
最近使用 RSA Authentication Manager, 并且与其自带的Radius server整合, RSA的Radius server 配置不太透明, 目前只配成功了PAP方式的验证,C ...
- jQuery的选择器中的通配符[id^='code']
1.选择器 (1)通配符: $("input[id^='code']");//id属性以code开始的所有input标签 $("input[id$='code']&quo ...
- 爱上MVC~AuthorizeAttribute验证不通过如何停止当前上下文
回到目录 我们知道mvc里有一些过滤器,AuthorizeAttribute用来做授权,一般在用户授权方面可以使用它,当使用没有登陆,我们直接跳到登陆页,这是没有问题的,可我要说的是,当用户对某个Ac ...
- 数据库MySQL开篇
作为一名前端,后天方面的知识虽说不一定要精通后台但是对后台方面的知识有一定了解是非常必要的. 今天先说一些概念方面的,因本人才疏学浅,对于数据库这方面的自学相当于摸着石头过河,有什么写的不对的地方,希 ...
- Atitit 发帖机系列(7) 词法分析的方法attilax大总结)
Atitit 发帖机系列(7) 词法分析的方法attilax大总结) 1.1. 词法分析貌似俩大方法,一个直接根据状态图转换,一个根据dfa1 1.2. switchcase或者ifelse 最原始方 ...
- Atitit 表达式原理 语法分析 原理与实践 解析java的dsl 递归下降是现阶段主流的语法分析方法
Atitit 表达式原理 语法分析 原理与实践 解析java的dsl 递归下降是现阶段主流的语法分析方法 于是我们可以把上面的语法改写成如下形式:1 合并前缀1 语法分析有自上而下和自下而上两种分析 ...
- js const
js const const 声明创建一个只读的常量.这不意味着常量指向的值不可变,而是变量标识符的值只能赋值一次. const state = { notes: [], activeNote: {} ...
- 【原创】NIO框架入门(一):服务端基于Netty4的UDP双向通信Demo演示
申明:本文由作者基于日常实践整理,希望对初次接触MINA.Netty的人有所启发.如需与作者交流,见文签名,互相学习. 学习交流 更多学习资料:点此进入 推荐 移动端即时通讯交流: 215891622 ...
- React(三)组件的生命周期
Component Specs and LifeCycle <div id="app"></div> <script src="bower_ ...
- HTTP协议从入门到大牛,初识HTTP协议(学习笔记)
HTTP数据传输协议 当访问一个网页时,浏览器会向服务器发起一条HTTP请求,接着服务器会去寻找相应的资源,如果请求成功,就会把这个对象,对象类型,对象长度以及其他的信息放在HTTP响应中,发送给客户 ...