MVC之前的那點事兒系列(2):HttpRuntime詳解分析(上)

來源:互聯網
上載者:User

標籤:c   style   class   blog   code   java   

文章內容

從上章文章都知道,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的執行個體,然後讓Unmanaged 程式碼調用。所以說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 environmentHostingEnvironment 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 insteadObjectHandle 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_PALGetDefaultDomainIdentity(),#endif // FEATURE_PALsetup);}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 featuresHttpRuntime.InitializeHostingFeatures(hostingFlags, policyLevel, appDomainCreationException);

 

該代碼進入HttpRuntime的靜態方法,接著調用HostingInt方法進行一些初始化工作,其中有一行代碼也是我們需要知道的,如下:

// Initialize the build managerBuildManager.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對象給Unmanaged 程式碼,其中包括了ISAPIRuntime的執行個體,代碼如下:

return new ObjectHandle(isapiRuntime);

 

Unmanaged 程式碼接受ObjectHandle對象以後,要幹什麼呢?我們且看下篇文章的繼續分析。

同步與推薦

本文已同步至目錄索引:MVC之前的那點事兒系列

MVC之前的那點事兒系列文章,包括了原創,翻譯,轉載等各類型的文章,如果對你有用,請推薦支援一把,給大叔寫作的動力。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.