MVC之前-ASP.NET初始化流程分析1
理论的解说一般都是枯燥的,研究一个代码框架更是如此,似乎除了对大量的源码加以解释之外无话可说,但是这又是不可缺少的,否则应用这样的框架起来总有不放心之感,总有不少的坑要踩。更进一步讲,一个好的框架应该给它的使用者足够的扩展空间(尤其是像MVC这样的基础框架),对框架本身没有很好的把握则对于框架的扩展似乎就只能寻找别人的“成功”经验了,然而生搬硬套从来不是解决问题的好方法,合理运用他人经验与创造性的解决方案都离不开对框架的深入理解,从这方面来说代码分析其实强于抽象的说明,因为代码的逻辑是确定的,代码的语言说服力其实强于书面语言(当然是指编写良好的代码)。当然对于代码之外的讨论也是非常必要甚至更加重要的,比如探讨框架设计的思路和模式,框架应用的相关实践乃至对框架的扩展等等。此一系列文章都是基于Asp.net Mvc框架的源码(包括Asp.net的一部分)对框架的基本设计与实现作出分析,另外参考了自己的一些项目以及当前一些优秀的开源项目(比如orchard)来讨论框架的应用与扩展。
Asp.net Mvc是当前使用比较多的web框架,也是比较先进的框架,目前.net的大部分源码都已经开放,这大大方便了我们对Asp.net Mvc的分析,下面就从Http请求进入Mvc框架处理之前的基本流程说起。
由于各IIS版本和工作模式(经典模式、集成模式)的不同,Http请求进入Asp.net的处理通道并不一样,这里不去细究里面的细节,就从创建应用程序域开始:
AppManagerAppDomainFactory分析
注:AppDomainFactory及AppManagerAppDomainFactory类在System.Web.Hosting中实现
在创建Appdomain时会调用IAppDomainFactory接口,该接口的实现如下:
public sealed class AppDomainFactory : IAppDomainFactory {
private AppManagerAppDomainFactory _realFactory;
public AppDomainFactory() {
_realFactory = new AppManagerAppDomainFactory();
}
public Object Create(String module, String typeName, String appId, String appPath,String strUrlOfAppOrigin, int iZone) {
return _realFactory.Create(appId, appPath);
}
}
该实现会调用AppManagerAppDomainFactory完成实际的创建过程。
public sealed class AppManagerAppDomainFactory : IAppManagerAppDomainFactory {
private ApplicationManager _appManager;
public AppManagerAppDomainFactory() {
_appManager = ApplicationManager.GetApplicationManager();
_appManager.Open();
}
public Object Create(String appId, String appPath) {
try {
if (appPath[0] == '.') {
System.IO.FileInfo file = new System.IO.FileInfo(appPath);
appPath = file.FullName;
}
if (!StringUtil.StringEndsWith(appPath, '\\')) {
appPath = appPath + "\\";
}
ISAPIRuntime isapiRuntime = (ISAPIRuntime)_appManager.CreateObjectInternal(appId, typeof(ISAPIRuntime), appHost,false, null);
isapiRuntime.StartProcessing();
return new ObjectHandle(isapiRuntime);
}
catch (Exception e) {
Debug.Trace("internal", "AppDomainFactory::Create failed with " + e.GetType().FullName + ": " + e.Message + "\r\n" + e.StackTrace);
throw;
}
}
}
代码的主要作用,就是通过ApplicationManager的CreateObjectInternal创建AppDomain,创建HostingEnvironment等,最终获取ISAPIRuntime的实例,然后让非托管代码调用。
ApplicationManager分析
注:ApplicationManager类在System.Web.Hosting中实现
首先看ApplicationManager类中的静态的创建ApplicationManager对象的方法:
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已经创建则可以直接返回当前HostingEnvironment设置的ApplicationManager,如果没有则创建新的ApplicationManager实例。
然后看ApplicationManager的CreateObjectInternal方法:
internal IRegisteredObject CreateObjectInternal(String appId, Type type, IApplicationHost appHost, bool failIfExists, HostingEnvironmentParameters hostingParameters) {
if (!typeof(IRegisteredObject).IsAssignableFrom(type))
throw new ArgumentException(SR.GetString(SR.Not_IRegisteredObject, type.FullName), "type");
HostingEnvironment env = GetAppDomainWithHostingEnvironment(appId, appHost, hostingParameters);
ObjectHandle h = env.CreateWellKnownObjectInstance(type.AssemblyQualifiedName, failIfExists);
return (h != null) ? h.Unwrap() as IRegisteredObject : null;
}
首先要先取得HostingEnvironment的实例,然后通过该实例的CreateWellKnownObjectInstance方法返回上述Create方法需要的ISAPIRuntime的实例。先来看如何获取HostingEnvironment实例的方法GetAppDomainWithHostingEnvironment。
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;
}
}
首先会检查字典是否会有已经存在的HostingEnvironment实例,如果有就返回,没有就会创建一个新的并保存到字典中,查看相关代码发现最终会调用ApplicationManager的私有方法CreateAppDomainWithHostingEnvironment创建AppDomain和HostingEnvironment。
private HostingEnvironment CreateAppDomainWithHostingEnvironment(String appId, IApplicationHost appHost, HostingEnvironmentParameters hostingParameters)
{
……
appDomain = AppDomain.CreateDomain(domainId,GetDefaultDomainIdentity(),setup);
……
Type hostType = typeof(HostingEnvironment);
String module = hostType.Module.Assembly.FullName;
String typeName = hostType.FullName;
ObjectHandle h = null;
……
try {
h = Activator.CreateInstance(appDomain, module, typeName);
}
……
HostingEnvironment env = (h != null) ? h.Unwrap() as HostingEnvironment : null;
if (env == null)
throw new SystemException(SR.GetString(SR.Cannot_create_HostEnv));
IConfigMapPathFactory configMapPathFactory = appHost.GetConfigMapPathFactory();
if (appDomainStartupConfigurationException == null) {
env.Initialize(this, appHost, configMapPathFactory, hostingParameters, policyLevel);
}
else {
env.Initialize(this, appHost, configMapPathFactory, hostingParameters, policyLevel, appDomainStartupConfigurationException);
}
return env;
}
可以看到代码创建了AppDomain和HostingEnvironment实例,创建HostingEnvironment实例以后,紧接着会调用其Initialize方法来进行初始化,然后返回对象实例。
HostingEnvironment分析
Initialize初始化方法,注意该方法的第一个参数是this,也就是ApplicationManager实例自身,这样会在HostingEnvironment中设置ApplicationManager,因而初始化之后可以通过HostingEnvironment获取ApplicationManager 。Initialize方法调用HttpRuntime的静态方法,进行一些初始化工作(其中会调用BuildManager的InitializeBuildManager方法进行初始化另外一些工作,其中包括编译App_Code目录下所有的.NET源代码)。最后如果HostingEnvironment初始化失败时会设置hostingInitFailed为true。
internal void Initialize(ApplicationManager appManager, IApplicationHost appHost, IConfigMapPathFactory configMapPathFactory, HostingEnvironmentParameters hostingParameters, PolicyLevel policyLevel, Exception appDomainCreationException)
{
……
_appManager = appManager;
……
HttpRuntime.InitializeHostingFeatures(hostingFlags,policyLevel,appDomainCreationException);
……
catch (Exception e) {
_hostingInitFailed = true;
}
}
创建HostingEnvironment后,会调用其方法CreateWellKnownObjectInstance创建ISAPIRuntime。创建好ISAPIRuntime实例后初始化工作就完成了第一个阶段,整个过程如下图所示:
理论的解说一般都是枯燥的,研究一个代码框架更是如此,似乎除了对大量的源码加以解释之外无话可说,但是这又是不可缺少的,否则应用这样的框架起来总有不放心之感,总有不少的坑要踩。更进一步讲,一个好的框架应该给它的使用者足够的扩展空间(尤其是像MVC这样的基础框架),对框架本身没有很好的把握则对于框架的扩展似乎就只能寻找别人的“成功”经验了,然而生搬硬套从来不是解决问题的好方法,合理运用他人经验与创造性的解决方案都离不开对框架的深入理解,从这方面来说代码分析其实强于抽象的说明,因为代码的逻辑是确定的,代码的语言说服力其实强于书面语言(当然是指编写良好的代码)。当然对于代码之外的讨论也是非常必要甚至更加重要的,比如探讨框架设计的思路和模式,框架应用的相关实践乃至对框架的扩展等等。此一系列文章都是基于Asp.net Mvc框架的源码(包括Asp.net的一部分)对框架的基本设计与实现作出分析,另外参考了自己的一些项目以及当前一些优秀的开源项目(比如orchard)来讨论框架的应用与扩展。
MVC之前-ASP.NET初始化流程分析1的更多相关文章
- ASP.NET初始化流程分析2
上一篇讲了从创建应用程序域到创建ISAPIRuntime实例的过程,本篇继续讲Asp.net处理第一次请求的必要的初始化过程. ISAPIRuntime分析 ISAPIRuntime在System.W ...
- u-boot中nandflash初始化流程分析(转)
u-boot中nandflash初始化流程分析(转) 原文地址http://zhuairlunjj.blog.163.com/blog/static/80050945201092011249136/ ...
- Spring MVC 处理一个请求的流程分析
Spring MVC是Spring系列框架中使用频率最高的部分.不管是Spring Boot还是传统的Spring项目,只要是Web项目都会使用到Spring MVC部分.因此程序员一定要熟练掌握MV ...
- Raid1源代码分析--初始化流程
初始化流程代码量比较少,也比较简单.主要是run函数.(我阅读的代码的linux内核版本是2.6.32.61) 四.初始化流程分析 run函数顾名思义,很简单这就是在RAID1开始运行时调用,进行一些 ...
- ASP.NET/MVC/Core的HTTP请求流程
ASP.NET HTTP管道(Pipeline)模型 1. 先讲一点,再深刻思考 一般我们都在写业务代码,优化页面,优化逻辑之间内徘徊.也许我们懂得HTTP,HTTPS的GET,POST,但是我们大部 ...
- 简述C#中IO的应用 RabbitMQ安装笔记 一次线上问题引发的对于C#中相等判断的思考 ef和mysql使用(一) ASP.NET/MVC/Core的HTTP请求流程
简述C#中IO的应用 在.NET Framework 中. System.IO 命名空间主要包含基于文件(和基于内存)的输入输出(I/O)服务的相关基础类库.和其他命名空间一样. System.I ...
- Asp.net MVC的Model Binder工作流程以及扩展方法(2) - Binder Attribute
上篇文章中分析了Custom Binder的弊端: 由于Custom Binder是和具体的类型相关,比如指定类型A由我们的Custom Binder解析,那么导致系统运行中的所有Action的访问参 ...
- Spring MVC启动流程分析
本文是Spring MVC系列博客的第一篇,后续会汇总成贴子. Spring MVC是Spring系列框架中使用频率最高的部分.不管是Spring Boot还是传统的Spring项目,只要是Web项目 ...
- ASP.NET MVC学前篇之请求流程
ASP.NET MVC学前篇之请求流程 请求流程描述 对于请求的流程,文章的重点是讲HttpApplication和HttpModule之间的关系,以及一个简单的示例实现.(HttpModule又是M ...
随机推荐
- .Net程序员学用Oracle系列(24):数据字典、死锁
1.静态数据字典 1.1.实用静态数据字典 1.2.运用静态数据字典 2.动态数据字典 2.1.实用动态性能视图 2.2.运用动态性能视图 3.死锁 3.1.定位死锁 3.2.解锁方法 3.3.强制删 ...
- Python输入一个数字打印等腰三角形
要求 用户输入一个数字,按照数字打印出等腰三角形 思路 1,用户输入的数字为n代表一共有多少行 2,使用一个循环带两个for循环,第一层循环是循环行数,第二层两个平行for循环一个打印空格一个打印*号 ...
- swap与dd命令使用详解
处理交换文件和分区 交换分区是系统RAM 的补充 基本设置包括: 创建交换分区或者文件 使用mkswap 写入特殊签名 在/etc/fstab 文件中添加适当的条目 使用swapon -a 挂载交换分 ...
- Linux文件属性及如何修改文件属性
ls -al:显示文件的文件名与相关属性并列出所有文件详细的权限与属性 dr-xr-x---. 7 root root 4096 Apr3 12:31 ...
- hive的表的基本操作
环境简介 实验环境使用的是cloudera-quickstart-vm-5.0环境. 内容摘要 创建表 修改表名 修改表中的列名 添加列 删除列 替换列 正文 Alter Table 语句 上面所述的 ...
- 《Android进阶》之第三篇 深入理解android的消息处理机制
Android 异步消息处理机制 让你深入理解 Looper.Handler.Message三者关系 android的消息处理机制(图+源码分析)——Looper,Handler,Message an ...
- 开涛spring3(5.3) - Spring表达式语言 之 5.3 SpEL语法
5.3 SpEL语法 5.3.1 基本表达式 一.字面量表达式: SpEL支持的字面量包括:字符串.数字类型(int.long.float.double).布尔类型.null类型. 类型 示例 字 ...
- python——爬虫&问题解决&思考(1)
最近刚接触python,找点小任务来练练手,希望自己在实践中不断的锻炼自己解决问题的能力.这个小爬虫来自慕课网的一门课程,我在这里记录的是自己学习的过程中遇到的问题和解决方法以及爬虫之外的思考. 这次 ...
- 一不小心,陷入TCP的性能问题
一.现象 在一次访问请求nginx中,通常只需要几毫秒的RT,但当请求数据达到某一个数值时,rt明显提高,甚至超过了300毫秒. 二.问题的原因 大家都知道,TCP为了提高带宽利用率和吞吐量,做了各种 ...
- WPF界面XAML中的if……else……
xaml本身并不支持if--else--,要用Converter替代if--else--来实现我们想要的效果,知者请速离开,不要浪费时间 需求:按照Window的WindowState来决定Gri ...