開題 既然決定了開始寫博客,那就從讀ASP.NET core的源碼開始吧!對於我這個不怎麼善於寫文章的人來說,也算是鍛煉鍛煉自己歸納總結能力。 千里之行,始於足下 俗話說,“千里之行,始於足下”,我們先看看ASP.NET core的 是如何使用的。為了方便說明,這裡建立的 的測試工程,使用 ...
開題
既然決定了開始寫博客,那就從讀ASP.NET core的源碼開始吧!對於我這個不怎麼善於寫文章的人來說,也算是鍛煉鍛煉自己歸納總結能力。
千里之行,始於足下
俗話說,“千里之行,始於足下”,我們先看看ASP.NET core的Configuration
是如何使用的。為了方便說明,這裡建立的xunit
的測試工程,使用nuget
下載Microsoft.Extensions.Configuration
包:
var dc = new Dictionary<string, string>
{
{"SectionA", "ValueA" },
{"SectionB", "ValueB" }
};
var builder = new ConfigurationBuilder().AddInMemoryCollection(dc);
var configurationRoot = builder.Build();
Assert.Equal("ValueA", configurationRoot["SectionA"]);
Assert.Equal("ValueB", configurationRoot["SectionB"]);
最簡單的幾行代碼,可以看出配置文件最基本的使用流程。
構建一個
ConfigurationBuilder
對象添加配置資源(這裡使用的是
InMemoryCollection
)最後使用
Build()
方法生成IConfigurationRoot
配置對象使用
IConfigurationRoot
對象獲取配置信息
這裡我們從這段代碼中能看出ConfigurationBuilder
是繼承了IConfigurationBuilder
這個介面,它Build
出來的對象繼承了IConfigurationRoot
介面,IConfigurationRoot
又繼承了IConfiguration
介面,而這些介面都位於Microsoft.Extensions.Configuration.Abstractions
這個程式集中。
那就從這裡開始抽絲剝繭,深入探究下配置部分的源碼吧!請從Github下載好源碼。
https://github.com/aspnet/Configuration => https://github.com/dotnet/extensions
你是不是發現最新的src文件夾下根本沒有我們想要的Microsoft.Extensions
等源碼呀!我們需要切換到v3.1.3
或以下的版本然後下載對應版本的代碼就好了。為了更方便調試代碼,我建議還是將代碼從Github的倉庫clone下來,構建源碼也比較簡單。
git clone https://github.com/dotnet/extensions.git
git checkout v3.1.2
restore.cmd
build.cmd
從Microsoft.Extensions.Configuration.Abstraction
開始
不說廢話,上圖:
從上圖可以看出我們的IConfigurationBuilder
中包含一個IList<IConfigurationSource>
對象和Add(IConfigurationSource source)
方法 ,這意味著ASP.NET core的配置對象是支持多配置源的。 IConfigurationBuilder
通過Build
方法,生成一個IConfigurationRoot
對象,可通過實現父介面IConfiguration
的索引器this[string key]
獲取到配置信息。對於IConfigurationProvider
對象和IConfigurationSection
我們通過對抽象的實現去探索。
進入Microsoft.Extensions.Configuration
ConfigurationBuilder
`Build`方法最終生成了`ConfigurationRoot`對象,並初始了`List<IConfigurationProvider>`。
public IConfigurationRoot Build()
{
var providers = new List<IConfigurationProvider>();
foreach (var source in Sources)
{
var provider = source.Build(this);
providers.Add(provider);
}
return new ConfigurationRoot(providers);
}
而IConfigurationProvider
是由IConfigurationSource
build生成的。
public IConfigurationBuilder Add(IConfigurationSource source)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
Sources.Add(source);
return this;
}
ConfigurationRoot
那配置信息時如何載入進去的呢?
我們先看下配置信息是如何讀取的,在索引器的get
方法中,數據時是從provider
中獲取到的,而且在所有的_providers
中倒序查找到provider
後就會退出查找,這也意味著我們在添加多個配置源時,最後添加的配置源會覆蓋之前添加的配置源,當然這是在鍵值相同的情況下。
public string this[string key]
{
get
{
for (var i = _providers.Count - 1; i >= 0; i--)
{
var provider = _providers[i];
if (provider.TryGet(key, out var value))
return value;
}
return null;
}
set...
}
那provider
的數據是從哪裡來的呢?在ConfigurationProvider
這個類中,可以看到所有的數據都來源於Data
這個Dictionary<string, string>
字典集合。
這個字典集合是什麼時候載入的呢?
public virtual bool TryGet(string key, out string value)
=> Data.TryGetValue(key, out value);
在構造ConfigurationRoot
對象時,我們看到所有的provider
對象都調用了Load()
方法。在ConfigurationProvider
類中Load()
方法是虛方法,且沒有找到別的地方對Data
這個變數進行賦值,那麼這個時候可以猜想ConfigurationProvider
繼承類會重寫這個方法,載入Data
的值,那麼我們去MemoryConfigurationProvider
這個類中去驗證一下,MemoryConfigurationProvider
在構造函數中完成了Data
的賦值,沒有重寫這個方法。汗~~~
public ConfigurationRoot(IList<IConfigurationProvider> providers)
{
if (providers == null)
{
throw new ArgumentNullException(nameof(providers));
}
_providers = providers;
_changeTokenRegistrations = new List<IDisposable>(providers.Count);
foreach (var p in providers)
{
p.Load();
_changeTokenRegistrations.Add(ChangeToken.OnChange(() =>p.GetReloadToken(),
() => RaiseChanged()));
}
}
那在這裡先去漫游一下,通過FileConfigurationProvider
=> JsonConfigurationProvider
找到了Data = JsonConfigurationFileParser.Parse(stream)
,證明我們的猜想還是沒有錯的。
這麼一路抽絲剝繭,我們就知道了配置信息是如何運作的了!下一步我們看看配置文件是如何監視文件變化的,也對FileConfigurationProvider
這一部分細化閱讀一下。