HttpRuntime详解分析
HttpRuntime详解分析(上)
文章内容
从上章文章都知道,asp.net是运行在HttpRuntime里的,但是从CLR如何进入HttpRuntime的,可能大家都不太清晰。本章节就是通过深入分析.Net4的源码来展示其中的重要步骤。请先看下图:

首先,CLR在初始化加载的时候,会加载一个非常重要的类AppManagerAppDomainFactory,这个类是做什么用的呢?首先这个类继承了IAppManagerAppDomainFactory接口,而这个接口是是有个可供COM调用的Create方法,代码如下:
[ComImport, Guid("02998279-7175-4d59-aa5a-fb8e44d4ca9d"), System.Runtime.InteropServices.InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)]
public interface IAppManagerAppDomainFactory {
#if !FEATURE_PAL // FEATURE_PAL does not enable COM
[return: MarshalAs(UnmanagedType.Interface)]
#else // !FEATURE_PAL
Object Create(String appId, String appPath);
#endif // !FEATURE_PAL
Object Create([In, MarshalAs(UnmanagedType.BStr)] String appId,
[In, MarshalAs(UnmanagedType.BStr)] String appPath);
void Stop();
}
我们来细看一下这个AppManagerAppDomainFactory是如何实现这个接口的,首先该类在默认的构造函数里,获取了一个ApplicationManager的实例用于在Create方法里使用。代码如下:
[SecurityPermission(SecurityAction.Demand, Unrestricted=true)]
public AppManagerAppDomainFactory() {
_appManager = ApplicationManager.GetApplicationManager();
_appManager.Open();
}
回到实现接口的Create方法,我们来看最重要的3行代码:
ISAPIApplicationHost appHost = new ISAPIApplicationHost(appId, appPath, false /*validatePhysicalPath*/);
ISAPIRuntime isapiRuntime = (ISAPIRuntime)_appManager.CreateObjectInternal(appId, typeof(ISAPIRuntime), appHost,
false /*failIfExists*/, null /*hostingParameters*/);
isapiRuntime.StartProcessing();
代码的主要作用,就是通过ApplicationManager的CreateObjectInternal一系列操作,最终获取ISAPIRuntime的实例,然后让非托管代码调用。所以说CreateObjectInternal方法在这里发挥了至关重要的功能:创建AppDomain,创建HostingEnvironment等一系列操作。
首先来看看AppManagerAppDomainFactory的构造函数,其里面调用的ApplicationManager. GetApplicationManager()方法是一个单例的实现,代码如下:
public static ApplicationManager GetApplicationManager() {
if (_theAppManager == null) {
lock (_applicationManagerStaticLock) {
if (_theAppManager == null) {
if (HostingEnvironment.IsHosted)
_theAppManager = HostingEnvironment.GetApplicationManager();
if (_theAppManager == null)
_theAppManager = new ApplicationManager();
}
}
}
return _theAppManager;
}
从代码看,大家可能有点疑惑,为什么HostingEnvironment属性IsHosted为true的时候会调用它的静态方法GetApplicationManager()来获取ApplicationManager的实例,这是因为ApplicationManager在后续的步骤创建HostingEnvironment对象并初始化的时候,将this自动传递给了HostingEnvironment对象实例(稍后在细说这个事情)。
回头再来看ApplicationManager实例的CreateObjectInternal方法,部分代码如下:
// get hosting environment HostingEnvironment env = GetAppDomainWithHostingEnvironment(appId, appHost, hostingParameters); // create the managed object in the worker app domain // When marshaling Type, the AppDomain must have FileIoPermission to the assembly, which is not // always the case, so we marshal the assembly qualified name instead ObjectHandle h = env.CreateWellKnownObjectInstance(type.AssemblyQualifiedName, failIfExists); return (h != null) ? h.Unwrap() as IRegisteredObject : null;
通过代码我们可以看到,首先要先得到HostingEnvironment的实例,然后通过该实例的CreateWellKnownObjectInstance方法返回上述Create方法需要的ISAPIRuntime的实例。那我们应该能想到GetAppDomainWithHostingEnvironment有2个作用,其一是先要获取AppDomain,其二是获取HostingEnvironment实例,来看看代码是否如我们猜想的结果,先来看代码:
private HostingEnvironment GetAppDomainWithHostingEnvironment(String appId, IApplicationHost appHost, HostingEnvironmentParameters hostingParameters) {
LockableAppDomainContext ac = GetLockableAppDomainContext (appId);
lock (ac) {
HostingEnvironment env = ac.HostEnv;
if (env != null) {
try {
env.IsUnloaded();
} catch(AppDomainUnloadedException) {
env = null;
}
}
if (env == null) {
env = CreateAppDomainWithHostingEnvironmentAndReportErrors(appId, appHost, hostingParameters);
ac.HostEnv = env;
Interlocked.Increment(ref _accessibleHostingEnvCount);
}
return env;
}
}
代码告诉我们,首先会检查是否会有已经存在的AddDomain以及相应的HostingEnvironment实例,如果有返回,没有就会创建一个新的。通过辗转调用,最终来到一个私有方法CreateAppDomainWithHostingEnvironment,在这个300行的私有方法里,有我们所迷惑已久的东西。
首先会有关于信任级别的代码,比如是运行在FullTrust上还是MiddleTrust上,这里会有相应的处理代码,由于我们这次代码分析的重点不在这里,所以具体代码就不细说了,来看看我们需要知道的代码段:
// Create the app domain
AppDomain appDomain = null;
// 此处省略很多代码
if (isLegacyCas) {
appDomain = AppDomain.CreateDomain(domainId,
#if FEATURE_PAL // FEATURE_PAL: hack to avoid non-supported hosting features
null,
#else // FEATURE_PAL
GetDefaultDomainIdentity(),
#endif // FEATURE_PAL
setup);
}
else {
appDomain = AppDomain.CreateDomain(domainId,
#if FEATURE_PAL // FEATURE_PAL: hack to avoid non-supported hosting features
null,
#else // FEATURE_PAL
GetDefaultDomainIdentity(),
#endif // FEATURE_PAL
setup,
permissionSet,
fullTrustAssemblies /* fully trusted assemblies list: null means only trust GAC assemblies */);
}
通过代码可以看出,这就是传说中创建AppDomain的地方,后续所有的东西比如HttpRuntime, HttpContext都是依托于这个AppDomain,这就是为什么HttpContext为什么不能在多站点共享,而能安全存在于AppDomain的原因。
继续往下看,在创建AppDomain的代码之后有几行这样的代码:
Type hostType = typeof(HostingEnvironment);
String module = hostType.Module.Assembly.FullName;
String typeName = hostType.FullName;
ObjectHandle h = null;
// 此处省略很多代码
h = Activator.CreateInstance(appDomain, module, typeName);
// 此处省略很多代码
HostingEnvironment env = (h != null) ? h.Unwrap() as HostingEnvironment : null;
// 此处省略很多代码
if (appDomainStartupConfigurationException == null) {
env.Initialize(this, appHost, configMapPathFactory, hostingParameters, policyLevel);
}
else {
env.Initialize(this, appHost, configMapPathFactory, hostingParameters, policyLevel, appDomainStartupConfigurationException);
}
return env;
这就是创建HostingEnvironment实例的地方,创建实例以后,紧接着会调用Initialize方法来进行初始化,然后返回对象实例(注意该方法的第一个参数哦,是this,也就是ApplicationManager实例自身,就解释了上面我所说的那个为什么能通过HostingEnvironment的静态方法GetApplicationManager()来获取ApplicationManager实例了)。
通过这些代码,我们就可以简单的知道了,如何获取ISAPIRuntime实例,从而为进入HttpRuntime做准备了。但是,我依然好奇HostingEnvironment的Initialize初始化方法到底都做了什么,好吧,我们来看看。
OK,瞄到了一行重要的代码:
// initiaze HTTP-independent features HttpRuntime.InitializeHostingFeatures(hostingFlags, policyLevel, appDomainCreationException);
该代码进入HttpRuntime的静态方法,接着调用HostingInt方法进行一些初始化工作,其中有一行代码也是我们需要知道的,如下:
// Initialize the build manager BuildManager.InitializeBuildManager();
该BuildManager的InitializeBuildManager方法,会调用自己的Initialize方法进行初始化另外一些工作,其中包括编译App_Code目录下所有的.NET源代码。由上面的一系列介绍我们知道,在一个AppDomain内,只有一个HostringEnvironment,所以该这个BuildManager的Initialize也就只执行一次,从而保证了编译不出问题(源码的注释也是这么说的哦)。
另外HostingInit方法里在初始化失败的时候,在catch里有一行非常特殊的代码:
_hostingInitFailed = true;
这是说在创建HostingEnvironment失败的时候,会给HttpRuntime的HostingInitFailed赋值为True。后面的章节所讨论的PreApplicationStartMethodAttribute的概念和WebActivator的入口都和这个值有关系,现在先不做讨论,后面章节细再说。
好了,回到AppManagerAppDomainFactory的Create方法,在得到ISAPIRuntime的实例,并且运行StartProcessing方法以后,会返回一个ObjectHandle对象给非托管代码,其中包括了ISAPIRuntime的实例,代码如下:
return new ObjectHandle(isapiRuntime);
非托管代码接受ObjectHandle对象以后,要干什么呢?我们且看下篇文章的继续分析。
同步与推荐
本文已同步至目录索引:MVC之前的那点事儿系列
MVC之前的那点事儿系列文章,包括了原创,翻译,转载等各类型的文章,如果对你有用,请推荐支持一把,给大叔写作的动力。
HttpRuntime详解分析的更多相关文章
- MVC之前的那点事儿系列(2):HttpRuntime详解分析(上)
文章内容 从上章文章都知道,asp.net是运行在HttpRuntime里的,但是从CLR如何进入HttpRuntime的,可能大家都不太清晰.本章节就是通过深入分析.Net4的源码来展示其中的重要步 ...
- MVC之前的那点事儿系列(3):HttpRuntime详解分析(下)
文章内容 话说,经过各种各样复杂的我们不知道的内部处理,非托管代码正式开始调用ISPAIRuntime的ProcessRequest方法了(ISPAIRuntime继承了IISPAIRuntime接口 ...
- lspci详解分析
lspci详解分析 一.PCI简介 PCI是一种外设总线规范.我们先来看一下什么是总线:总线是一种传输信号的路径或信道.典型情况是,总线是连接于一个或多个导体的电气连线,总 线上连接的所有设备可在同一 ...
- JavaScript 内存详解 & 分析指南
前言 JavaScript 诞生于 1995 年,最初被设计用于网页内的表单验证. 这些年来 JavaScript 成长飞速,生态圈日益壮大,成为了最受程序员欢迎的开发语言之一.并且现在的 JavaS ...
- shiro过滤器详解分析
(原) shiro最核心的2个操作,一个是登录的实现,一就是过滤器了.登录有时间再补录说明,这里分析下shiro过滤器怎样玩的. 1.目标 这里会按如下顺序逐一看其实原理,并尽量找出其出处. 先看一下 ...
- GBDT 详解分析 转+整理
GBDT DT 回归树 Regression Decision Tree 梯度迭代 GBDT工作过程实例 需要解释的三个问题 - 既然图1和图2 最终效果相同,为何还需要GBDT呢? - Gradie ...
- Javascript异步编程之setTimeout与setInterval详解分析(一)
Javascript异步编程之setTimeout与setInterval 在谈到异步编程时,本人最主要会从以下三个方面来总结异步编程(注意:特别解释:是总结,本人也是菜鸟,所以总结不好的,请各位大牛 ...
- [Android 测试] 压力稳定性测试之: Monkey 详解分析脚本(转载)
一.什么是稳定性测试? 通过随机点击屏幕一段时间,看看app会不会奔溃,能不能维持正常运行. 二. Money是什么? Monkey测试是Android平台自动化测试的一种手段,通过Monkey程序模 ...
- oracle游标透彻详解分析
主:本文来自:http://www.cnblogs.com/huyong/archive/2011/05/04/2036377.html 4.1 游标概念 4.1.1 处理显式游标 4.1.2 处理隐 ...
随机推荐
- 解决SQL订阅过程中找不到已经创建的订阅
原文:解决SQL订阅过程中找不到已经创建的订阅 之前有写过一篇博客,主要是图解SQL复制技术:图解SQL 2008数据库复制,当时的测试环境是在我本地同一个服务器上面,所以测试的时候可谓是一帆风顺,最 ...
- UVALive 5099 Nubulsa Expo 全球最小割 非网络流量 n^3
主题链接:点击打开链接 意甲冠军: 给定n个点m条无向边 源点S 以下m行给出无向边以及边的容量. 问: 找一个汇点,使得图的最大流最小. 输出最小的流量. 思路: 最大流=最小割. 所以题意就是找全 ...
- Jquery基础教程第二版学习记录
本文仅为个人jquery基础的学习,简单的记录以备忘. 在线手册:http://www.php100.com/manual/jquery/第一章:jquery入门基础jquery知识:jquery能做 ...
- AES加密CBC模式兼容互通四种编程语言平台【PHP、Javascript、Java、C#】
原文:AES加密CBC模式兼容互通四种编程语言平台[PHP.Javascript.Java.C#] 由于本人小菜,开始对AES加密并不了解,在网络上花了比较多时间查阅资料整理: 先简单从百度找来介绍: ...
- C++ 对象模型具体评论(特别easy理解力)
c++对象模型系列 转 一.指针与引用 一 概括 指针和引用,在C++的软件开发中非经常见,假设能恰当的使用它们可以极大的提 高整个软件的效率,可是非常多的C++学习者对它们的各种使用情况并非都了解, ...
- 【Unity 3D】学习笔记三十八:角色控制器
角色控制器 在unity中,已经帮我们实现的上下左右跳等动作,并将他们封装成了角色控制器.角色控制器保存在unity标准资源包中,能够说是很的强大.能够模拟第一或者第三人称视角.不受刚体的限制,很适用 ...
- unicode编码和中国的相互转换
如果你的原始文件1.properties(该文件的编码中国).要转换unicode的 在cmd通过进入你在哪里在这种类型的文件夹: native2ascii -encoding gb2312 1.pr ...
- 用python 爬取网页图片
import re import string import sys import os import urllib url="http://tieba.baidu.com/p/252129 ...
- 解决OUTLOOK 533错误问题
OutLook中“553 sorry, that domain isn‘t in my list of allowed rcpthosts (#5.7.1)”,无法发送邮件错误,解决方法 最近我在给徐 ...
- 随记一个C的毫秒级群PING
正好公司为了检测前台网络,力图收集有力证据与某CDN PK,所以随手写了一个群PING的程序. 写的内容比较简单,没有去特别追求线程效率,也没有去用LINUX 2.6+的殿堂级神器,以追求实现效率为主 ...