抽絲剝繭讀源碼——Microsoft.Extensions.Configuration(1)

開題

​ 既然決定了開始寫博客,那就從讀ASP.NET core的源碼開始吧!對於我這個不怎麼善於寫文章的人來講,也算是鍛鍊鍛鍊本身概括總結能力。git

千里之行,始於足下

​ 俗話說,「千里之行,始於足下」,咱們先看看ASP.NET core的Configuration是如何使用的。爲了方便說明,這裏創建的xunit的測試工程,使用nuget下載Microsoft.Extensions.Configuration包:github

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"]);

​ 最簡單的幾行代碼,能夠看出配置文件最基本的使用流程。c#

構建一個ConfigurationBuilder對象ide

添加配置資源(這裏使用的是InMemoryCollection)函數

最後使用Build()方法生成IConfigurationRoot配置對象測試

使用IConfigurationRoot對象獲取配置信息ui

​ 這裏咱們從這段代碼中能看出ConfigurationBuilder是繼承了IConfigurationBuilder這個接口,它Build出來的對象繼承了IConfigurationRoot接口,IConfigurationRoot又繼承了IConfiguration接口,而這些接口都位於Microsoft.Extensions.Configuration.Abstractions這個程序集中。this

​ 那就從這裏開始抽絲剝繭,深刻探究下配置部分的源碼吧!請從Github下載好源碼。調試

https://github.com/aspnet/Configuration => https://github.com/dotnet/extensionsrest

​ 你是否是發現最新的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是由IConfigurationSourcebuild生成的。

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這一部分細化閱讀一下。

相關文章
相關標籤/搜索