1. 程式人生 > >MVC之前的那點事兒系列(2):HttpRuntime詳解分析(上)

MVC之前的那點事兒系列(2):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之前事兒系列2HttpRuntime分析

文章內容 從上章文章都知道,asp.net是執行在HttpRuntime裡的,但是從CLR如何進入HttpRuntime的,可能大家都不太清晰。本章節就是通過深入分析.Net4的原始碼來展示其中的重要步驟。請先看下圖:   首先,CLR在初始化載入的時候,會載入一個非常重要的類AppManagerApp

MVC之前事兒系列3HttpRuntime分析

文章內容 話說,經過各種各樣複雜的我們不知道的內部處理,非託管程式碼正式開始呼叫ISPAIRuntime的ProcessRequest方法了(ISPAIRuntime繼承了IISPAIRuntime介面,該介面可以和COM進行互動,並且暴露了ProcessRequest介面方法)。至於為什麼要呼叫這個方法,

MVC之前事兒系列8UrlRouting的理解

文章內容 根據對Http Runtime和Http Pipeline的分析,我們知道一個ASP.NET應用程式可以有多個HttpModuel,但是隻能有一個HttpHandler,並且通過這個HttpHandler的BeginProcessRequest(或ProcessRequest)來處理並返回請求,前

MVC之前事兒系列1:進入CLR

MVC之前的那點事兒系列,是筆者在2012年初閱讀MVC3原始碼的時候整理的,主要講述的是從HTTP請求道進入MVCHandler之前的內容,包括了原創,翻譯,轉載,整理等各型別文章,當然也參考了部落格園多位大牛的文章,對此表示感謝,這次有時間貼出來,希望對大家有用。 主要內容 本文講解的是:伺服器接受H

MVC之前事兒系列7WebActivator的實現原理

文章內容 上篇文章,我們分析如何動態註冊HttpModule的實現,本篇我們來分析一下通過上篇程式碼原理實現的WebActivator類庫,WebActivator提供了3種功能,允許我們分別在HttpApplication初始化之前,之後以及ShutDown的時候分別執行指定的程式碼,示例如下: [

MVC之前事兒系列9MVC如何在Pipeline中接管請求的?

文章內容 上個章節我們講到了,可以在HttpModules初始化之前動態新增Route的方式來自定義自己的HttpHandler,最終接管請求的,那MVC是這麼實現的麼?本章節我們就來分析一下相關的MVC原始碼來驗證一下我們的這個問題。 先建立一個MVC3的Web Application,選擇預設的模

MVC之前事兒系列4Http Pipeline詳細分析

文章內容 繼續上一章節的內容,通過HttpApplicationFactory的GetApplicationInstance靜態方法獲取例項,然後執行該例項的BeginProcessRequest方法進行執行餘下的Http Pipeline 操作,程式碼如下: // Get application i

MVC之前事兒系列5Http Pipeline詳細分析

文章內容 接上面的章節,我們這篇要講解的是Pipeline是執行的各種事件,我們知道,在自定義的HttpModule的Init方法裡,我們可以新增自己的事件,比如如下程式碼: public class Test : IHttpModule { public void Init(HttpAp

MVC之前事兒系列6動態註冊HttpModule

文章內容 通過前面的章節,我們知道HttpApplication在初始化的時候會初始化所有配置檔案裡註冊的HttpModules,那麼有一個疑問,能否初始化之前動態載入HttpModule,而不是隻從Web.config裡讀取? 答案是肯定的, ASP.NET MVC3釋出的時候提供了一個Microsof

MVC之前事兒系列10MVC為什麼不再需要註冊萬用字元*.*了?

文章內容 很多教程裡都提到了,在部署MVC程式的時候要配置萬用字元對映(或者是*.mvc)到aspnet_ISPAI.dll上,在.NET4.0之前確實應該這麼多,但是.NET4.0之後已經不要再費事了,因為它預設就支援了。 你可以會問,沒有對映配置,請求這麼可能會走到aspnet_ISPAI.dll

MVC之前事兒 ---- 系列文章

需要 cnblogs post omx pip 實現原理 內容 activator div MVC之前的那點事兒系列,是筆者在2012年初閱讀MVC3源碼的時候整理的,主要講述的是從HTTP請求道進入MVCHandler之前的內容,包括了原創,翻譯,轉載,整理等各類型文

SpringBoot 2.0 系列流程

寫在前面 本節將詳細介紹如何使用Spring Boot。它涵蓋了諸如專案管理及自動構建工具、自動配置以及如何執行應用程式等主題。我們還介紹了一些Spring Boot最佳實踐。Spring Boot沒有什麼特別之處(它只是另一個我們可以使用的庫),但是有一些約

解讀ASP.NET 5 & MVC6系列6Middleware

在第1章專案結構分析中,我們提到Startup.cs作為整個程式的入口點,等同於傳統的Global.asax檔案,即:用於初始化系統級的資訊(例如,MVC中的路由配置)。本章我們就來一一分析,在這裡如何初始化這些系統級的資訊。 新舊版本之間的Pipeline區別 ASP.NET 5和之前版本的最大區別是對HT

EventBus原始碼分析執行緒模型分析2.4版本

EventBus有四種執行緒模型 PostThread模式不需執行緒切換,直接在釋出者執行緒進行事件處理。 MainThread模式分類討論:釋出者執行緒是主執行緒則直接呼叫事件處理方法,否則通過Handler進行執行緒切換,切換到主執行緒處理事件,該模

前端基礎進階圖例道setTimeout與迴圈閉包的經典面試題

配圖與本文無關 我在詳細圖解作用域鏈與閉包一文中的結尾留下了一個關於setTimeout與迴圈閉包的思考題。 利用閉包,修改下面的程式碼,讓迴圈輸出的結果依次為1, 2, 3, 4, 5 for (var i=1; i<=5; i++) { setTimeout( function ti

算法筆記復雜度分析

n+1 增長 角度 復雜 判斷 and 就是 ret 執行時間 (一)漸進符號(這裏暫時只考慮大O) 以輸入規模n為自變量建立的時間復雜度實際上還是較復雜的,例如an2+bn+c+1,不僅與輸入規模有關,還與系統a、b和c有關。此時對該函數進一步抽象,僅考慮運行時間的

java基礎學習總結二十五logback

為什麼使用logback logback大約有以下的一些優點: 核心重寫、測試充分、初始化記憶體載入更小,這一切讓logback效能和log4j相比有諸多倍的提升 logback非常自然地直接實現了slf4j,這個嚴格來說算不上優點,只是這樣,再理解slf4j的前提下會很容易理解

Dubbo概念篇Dubbo ,架構演變及優缺點

架構演變 單一應用框架(ORM) 當網站流量很小時,只需一個應用,將所有功能如下單支付等都部署在一起,以減少部署節點和成本。 缺點:單一的系統架構,使得在開發過程中,佔用的資源越來越多,而且隨著流量的增加越來越難以維護。 垂直應用框架(MVC) 垂直應用架構解決了單一應用架

C語言面向物件程式設計繼承

    C++ 中的繼承,從派生類與基類的關係來看(出於對比 C 與 C++,只說公有繼承): 派生類內部可以直接使用基類的 public 、protected 成員(包括變數和函式) 使用派生類的物件,可以像訪問派生類自己的成員一樣訪問基類的成員  對於被派生

zookeeper入門學習原理

一 .Zookeeper功能簡介 ZooKeeper 是一個開源的分散式協調服務,由雅虎建立,是 Google Chubby 的開源實現。 分散式應用程式可以基於 ZooKeeper 實現諸如資料釋出/訂閱、負載均衡、命名服務、分散式協 調/通知