理論的解說一般都是枯燥的,研究一個代碼框架更是如此,似乎除了對大量的源碼加以解釋之外無話可說,但是這又是不可缺少的,否則應(yīng)用這樣的框架起來總有不放心之感,總有不少的坑要踩。更進(jìn)一步講,一個好的框架應(yīng)該給它的使用者足夠的擴(kuò)展空間(尤其是像MVC這樣的基礎(chǔ)框架),對框架本身沒有很好的把握則對于框架的擴(kuò)展似乎就只能尋找別人的“成功”經(jīng)驗(yàn)了,然而生搬硬套從來不是解決問題的好方法,合理運(yùn)用他人經(jīng)驗(yàn)與創(chuàng)造性的解決方案都離不開對框架的深入理解,從這方面來說代碼分析其實(shí)強(qiáng)于抽象的說明,因?yàn)榇a的邏輯是確定的,代碼的語言說服力其實(shí)強(qiáng)于書面語言(當(dāng)然是指編寫良好的代碼)。當(dāng)然對于代碼之外的討論也是非常必要甚至更加重要的,比如探討框架設(shè)計的思路和模式,框架應(yīng)用的相關(guān)實(shí)踐乃至對框架的擴(kuò)展等等。此一系列文章都是基于Asp.net Mvc框架的源碼(包括Asp.net的一部分)對框架的基本設(shè)計與實(shí)現(xiàn)作出分析,另外參考了自己的一些項(xiàng)目以及當(dāng)前一些優(yōu)秀的開源項(xiàng)目(比如orchard)來討論框架的應(yīng)用與擴(kuò)展。
Asp.net Mvc是當(dāng)前使用比較多的web框架,也是比較先進(jìn)的框架,目前.net的大部分源碼都已經(jīng)開放,這大大方便了我們對Asp.net Mvc的分析,下面就從Http請求進(jìn)入Mvc框架處理之前的基本流程說起。
由于各IIS版本和工作模式(經(jīng)典模式、集成模式)的不同,Http請求進(jìn)入Asp.net的處理通道并不一樣,這里不去細(xì)究里面的細(xì)節(jié),就從創(chuàng)建應(yīng)用程序域開始:
AppManagerAppDomainFactory分析
注:AppDomainFactory及AppManagerAppDomainFactory類在System.Web.Hosting中實(shí)現(xiàn)
在創(chuàng)建Appdomain時會調(diào)用IAppDomainFactory接口,該接口的實(shí)現(xiàn)如下:
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); } }
該實(shí)現(xiàn)會調(diào)用AppManagerAppDomainFactory完成實(shí)際的創(chuàng)建過程。
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創(chuàng)建AppDomain,創(chuàng)建HostingEnvironment等,最終獲取ISAPIRuntime的實(shí)例,然后讓非托管代碼調(diào)用。
ApplicationManager分析
注:ApplicationManager類在System.Web.Hosting中實(shí)現(xiàn)
首先看ApplicationManager類中的靜態(tài)的創(chuàng)建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已經(jīng)創(chuàng)建則可以直接返回當(dāng)前HostingEnvironment設(shè)置的ApplicationManager,如果沒有則創(chuàng)建新的ApplicationManager實(shí)例。
然后看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的實(shí)例,然后通過該實(shí)例的CreateWellKnownObjectInstance方法返回上述Create方法需要的ISAPIRuntime的實(shí)例。先來看如何獲取HostingEnvironment實(shí)例的方法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; } }
首先會檢查字典是否會有已經(jīng)存在的HostingEnvironment實(shí)例,如果有就返回,沒有就會創(chuàng)建一個新的并保存到字典中,查看相關(guān)代碼發(fā)現(xiàn)最終會調(diào)用ApplicationManager的私有方法CreateAppDomainWithHostingEnvironment創(chuàng)建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; }
可以看到代碼創(chuàng)建了AppDomain和HostingEnvironment實(shí)例,創(chuàng)建HostingEnvironment實(shí)例以后,緊接著會調(diào)用其Initialize方法來進(jìn)行初始化,然后返回對象實(shí)例。
HostingEnvironment分析
Initialize初始化方法,注意該方法的第一個參數(shù)是this,也就是ApplicationManager實(shí)例自身,這樣會在HostingEnvironment中設(shè)置ApplicationManager,因而初始化之后可以通過HostingEnvironment獲取ApplicationManager 。Initialize方法調(diào)用HttpRuntime的靜態(tài)方法,進(jìn)行一些初始化工作(其中會調(diào)用BuildManager的InitializeBuildManager方法進(jìn)行初始化另外一些工作,其中包括編譯App_Code目錄下所有的.NET源代碼)。最后如果HostingEnvironment初始化失敗時會設(shè)置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; } }
創(chuàng)建HostingEnvironment后,會調(diào)用其方法CreateWellKnownObjectInstance創(chuàng)建ISAPIRuntime。創(chuàng)建好ISAPIRuntime實(shí)例后初始化工作就完成了第一個階段,整個過程如下圖所示:
理論的解說一般都是枯燥的,研究一個代碼框架更是如此,似乎除了對大量的源碼加以解釋之外無話可說,但是這又是不可缺少的,否則應(yīng)用這樣的框架起來總有不放心之感,總有不少的坑要踩。更進(jìn)一步講,一個好的框架應(yīng)該給它的使用者足夠的擴(kuò)展空間(尤其是像MVC這樣的基礎(chǔ)框架),對框架本身沒有很好的把握則對于框架的擴(kuò)展似乎就只能尋找別人的“成功”經(jīng)驗(yàn)了,然而生搬硬套從來不是解決問題的好方法,合理運(yùn)用他人經(jīng)驗(yàn)與創(chuàng)造性的解決方案都離不開對框架的深入理解,從這方面來說代碼分析其實(shí)強(qiáng)于抽象的說明,因?yàn)榇a的邏輯是確定的,代碼的語言說服力其實(shí)強(qiáng)于書面語言(當(dāng)然是指編寫良好的代碼)。當(dāng)然對于代碼之外的討論也是非常必要甚至更加重要的,比如探討框架設(shè)計的思路和模式,框架應(yīng)用的相關(guān)實(shí)踐乃至對框架的擴(kuò)展等等。此一系列文章都是基于Asp.net Mvc框架的源碼(包括Asp.net的一部分)對框架的基本設(shè)計與實(shí)現(xiàn)作出分析,另外參考了自己的一些項(xiàng)目以及當(dāng)前一些優(yōu)秀的開源項(xiàng)目(比如orchard)來討論框架的應(yīng)用與擴(kuò)展。
http://www.cnblogs.com/ssxg/p/7077187.html