关于Asp.net core配置信息读取的源码分析梳理
概述
我們都知道asp.net core配置信息的讀取離不開IConfigurationSource和IConfigurationProvider這兩個類,ConfigurationSource可以提供一個ConfigurationProvider,然后去讀取信息。究竟他們之間有著怎樣的千絲萬縷,我們一起來看看源碼。
首先我們來建立一個.net core控制臺項目,來運行以下代碼:
class Program{static void Main(string[] args){ConfigurationBuilder configBuilder = new ConfigurationBuilder();configBuilder.SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json");var configFile = configBuilder.Build();Console.ReadKey();}}短短幾行 代碼看起來很簡單,就是用來讀取appsettings.json文件中的配置信息。然而我們今天想搞清楚其背后運行的原理,要花點時間。
首先、我們根據代碼ConfigurationBuilder configBuilder = new ConfigurationBuilder();知道創建了一個configBuilder對象;
其次,configBuilder.SetBasePath(Directory.GetCurrentDirectory()) 該代碼的調用我們也能大概見名知義,獲取當前的目錄;
接下來,重點來了,configBuilder.AddJsonFile("appsettings.json")的實現究竟是怎樣的?我們來看下源碼的實現:
f12進去后源碼如下:
/// <summary>Extension methods for adding <see cref="T:Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider" />.</summary>public static class JsonConfigurationExtensions{/// <summary>Adds the JSON configuration provider at <paramref name="path" /> to <paramref name="builder" />.</summary>/// <param name="builder">The <see cref="T:Microsoft.Extensions.Configuration.IConfigurationBuilder" /> to add to.</param>/// <param name="path">Path relative to the base path stored in/// <see cref="P:Microsoft.Extensions.Configuration.IConfigurationBuilder.Properties" /> of <paramref name="builder" />.</param>/// <returns>The <see cref="T:Microsoft.Extensions.Configuration.IConfigurationBuilder" />.</returns>public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder,string path){return builder.AddJsonFile((IFileProvider) null, path, false, false);} }緊接著f12再看實現的源碼,依然在JsonConfigurationExtensions這個擴展類里面:
public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder,IFileProvider provider,string path,bool optional,bool reloadOnChange){if (builder == null)throw new ArgumentNullException(nameof (builder));if (string.IsNullOrEmpty(path))throw new ArgumentException(SR.Error_InvalidFilePath, nameof (path));return builder.AddJsonFile((Action<JsonConfigurationSource>) (s =>{s.FileProvider = provider;s.Path = path;s.Optional = optional;s.ReloadOnChange = reloadOnChange;s.ResolveFileProvider();}));}這時候有沒有發現builder.AddJsonFile((Action<JsonConfigurationSource>)這個方法里面出現了一個關鍵的信息點:JsonConfigurationSource (JsonConfigurationSource 繼承抽象類FileConfigurationSource,而FileConfigurationSource:IConfigurationSource) 。關系如下圖:
?看完上面這個關系圖后,我們緊接著上面builder.AddJsonFile()的實現源碼繼續f12往下,源碼如下:
//該代碼依然在JsonConfigurationExtensions類里面public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder,Action<JsonConfigurationSource> configureSource){return ConfigurationExtensions.Add<JsonConfigurationSource>(builder, (Action<M0>) configureSource);}我們看到上面的擴展方法實現是ConfigurationExtensions.Add...,再往下看實現:
public static class ConfigurationExtensions{/// <summary>Adds a new configuration source.</summary>/// <param name="builder">The <see cref="T:Microsoft.Extensions.Configuration.IConfigurationBuilder" /> to add to.</param>/// <param name="configureSource">Configures the source secrets.</param>/// <typeparam name="TSource" />/// <returns>The <see cref="T:Microsoft.Extensions.Configuration.IConfigurationBuilder" />.</returns>public static IConfigurationBuilder Add<TSource>(this IConfigurationBuilder builder,Action<TSource> configureSource)where TSource : IConfigurationSource, new(){TSource source = new TSource();if (configureSource != null)configureSource(source);return builder.Add((IConfigurationSource) source);} }到這里我們看到了其實就是IConfigurationBuilder調用了Add方法,添加了一個數據源(JsonConfigurationSource),至于JsonConfigurationSource類里面做了什么,我們看下實現。
public class JsonConfigurationSource : FileConfigurationSource{/// <summary>Builds the <see cref="T:Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider" /> for this source.</summary>/// <param name="builder">The <see cref="T:Microsoft.Extensions.Configuration.IConfigurationBuilder" />.</param>/// <returns>A <see cref="T:Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider" /></returns>public override IConfigurationProvider Build(IConfigurationBuilder builder){this.EnsureDefaults(builder);return (IConfigurationProvider) new JsonConfigurationProvider(this);}}JsonConfigurationSource類面的Build方法提供了一個JsonConfigurationProvider類,這里再貼下JsonConfigurationProvider類里面的代碼:
/// <summary>A JSON file based <see cref="T:Microsoft.Extensions.Configuration.FileConfigurationProvider" />.</summary>public class JsonConfigurationProvider : FileConfigurationProvider{/// <summary>Initializes a new instance with the specified source.</summary>/// <param name="source">The source settings.</param>public JsonConfigurationProvider(JsonConfigurationSource source): base((FileConfigurationSource) source){}/// <summary>Loads the JSON data from a stream.</summary>/// <param name="stream">The stream to read.</param>public virtual void Load(Stream stream){try{this.set_Data(JsonConfigurationFileParser.Parse(stream));}catch (JsonException ex){throw new FormatException(SR.Error_JSONParseError, (Exception) ex);}}}關于JsonConfigurationProvider里面的Load就是去讀取信息的實現,至于Load的具體實現我們不再深究。我們回到最初的控制臺configBuilder.Build(),看看其的實現:
public class ConfigurationBuilder : IConfigurationBuilder{/// <summary>Returns the sources used to obtain configuration values.</summary>public IList<IConfigurationSource> Sources { get; } = (IList<IConfigurationSource>) new List<IConfigurationSource>();/// <summary>Gets a key/value collection that can be used to share data between the <see cref="T:Microsoft.Extensions.Configuration.IConfigurationBuilder" />/// and the registered <see cref="T:Microsoft.Extensions.Configuration.IConfigurationProvider" />s.</summary>public IDictionary<string, object> Properties { get; } = (IDictionary<string, object>) new Dictionary<string, object>();/// <summary>Adds a new configuration source.</summary>/// <param name="source">The configuration source to add.</param>/// <returns>The same <see cref="T:Microsoft.Extensions.Configuration.IConfigurationBuilder" />.</returns>public IConfigurationBuilder Add(IConfigurationSource source){if (source == null)throw new ArgumentNullException(nameof (source));this.Sources.Add(source);return (IConfigurationBuilder) this;}/// <summary>Builds an <see cref="T:Microsoft.Extensions.Configuration.IConfiguration" /> with keys and values from the set of providers registered in/// <see cref="P:Microsoft.Extensions.Configuration.ConfigurationBuilder.Sources" />.</summary>/// <returns>An <see cref="T:Microsoft.Extensions.Configuration.IConfigurationRoot" /> with keys and values from the registered providers.</returns>public IConfigurationRoot Build(){List<IConfigurationProvider> configurationProviderList = new List<IConfigurationProvider>();foreach (IConfigurationSource source in (IEnumerable<IConfigurationSource>) this.Sources){IConfigurationProvider configurationProvider = source.Build((IConfigurationBuilder) this);configurationProviderList.Add(configurationProvider);}return (IConfigurationRoot) new ConfigurationRoot((IList<IConfigurationProvider>) configurationProviderList);}}看到這個源碼的時候有沒有種豁然開朗的感覺,前面我們說到IConfigurationBuilder調用了Add方法添加一個數據源,并沒說添加了一個數據源存在了哪里,到底有什么用處,現在在上面ConfigurationBuilder類里面看到存在了Sources 集合里面。然后configBuilder.Build()
去調用的時候遍歷數據源(Sources )集合,緊接著source (IConfigurationSource)調用了Build方法構建了一個configurationProvider對象存到configurationProviderList集合里面,最后在返回一個ConfigurationRoot對象的構造函數里面傳遞了configurationProviderList集合去執行。
貼上ConfigurationRoot的源碼:
public class ConfigurationRoot : IConfigurationRoot, IConfiguration, IDisposable{private readonly IList<IConfigurationProvider> _providers;private readonly IList<IDisposable> _changeTokenRegistrations;/// <summary>Initializes a Configuration root with a list of providers.</summary>/// <param name="providers">The <see cref="T:Microsoft.Extensions.Configuration.IConfigurationProvider" />s for this configuration.</param>public ConfigurationRoot(IList<IConfigurationProvider> providers){if (providers == null)throw new ArgumentNullException(nameof (providers));this._providers = providers;this._changeTokenRegistrations = (IList<IDisposable>) new List<IDisposable>(providers.Count);foreach (IConfigurationProvider provider in (IEnumerable<IConfigurationProvider>) providers){IConfigurationProvider p = provider;p.Load();this._changeTokenRegistrations.Add(ChangeToken.OnChange((Func<IChangeToken>) (() => p.GetReloadToken()), (Action) (() => this.RaiseChanged())));}} }public class ConfigurationRoot : IConfigurationRoot, IConfiguration, IDisposable{private readonly IList<IConfigurationProvider> _providers;private readonly IList<IDisposable> _changeTokenRegistrations;/// <summary>Initializes a Configuration root with a list of providers.</summary>/// <param name="providers">The <see cref="T:Microsoft.Extensions.Configuration.IConfigurationProvider" />s for this configuration.</param>public ConfigurationRoot(IList<IConfigurationProvider> providers){if (providers == null)throw new ArgumentNullException(nameof (providers));this._providers = providers;this._changeTokenRegistrations = (IList<IDisposable>) new List<IDisposable>(providers.Count);foreach (IConfigurationProvider provider in (IEnumerable<IConfigurationProvider>) providers){IConfigurationProvider p = provider;p.Load();this._changeTokenRegistrations.Add(ChangeToken.OnChange((Func<IChangeToken>) (() => p.GetReloadToken()), (Action) (() => this.RaiseChanged())));}} }看到沒,最后providers去調用了load方法。
結語
就上面的控制臺代碼來說IConfigurationSource對應的實現是JsonConfigurationSource;IConfigurationProvider,抽象類ConfigurationProvider對應的實現為JsonConfigurationProvider。如果我們要換成別的文件格式呢?比如ini,怎樣自定義配置源呢?大家可以先想想,其實也很簡單,下次跟大家分享。
最后說真的,.netCore源碼真的特別優秀,很值得花一番時間去看看!從其中可以學到許多架構知識!
—?—往日推薦—?—
github上star23k的程序員必備通用簡歷模板
Windows+.NetCore+git+IIS在Jenkins上的自動化部署入門
C#?創建單例你會幾種方式?
一個小時開發的直播推拉流軟件來了
關于數據庫建表是否建立外鍵的爭論,你有什么樣的觀點?
常見的23種設計模式彩圖過來了解一下
什么是線程同步?又如何解決線程同步問題?
使用.net?和Selenium模擬百度登錄
四種軟件架構,看看你屬于哪個層次?
.net爬蟲是一門必修課
.net下使用Selenium、PhantomJS
在.net平臺使用Quartz+Topshelf創建windows服務
極簡實用的Asp.NetCore模塊化框架決定免費開源了
關于Asp.Net?Core如何更完美地配置swagger
ASP.NET?Core如何自動生成小寫的破折號路由
總結
以上是生活随笔為你收集整理的关于Asp.net core配置信息读取的源码分析梳理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用 dotnet format 格式化
- 下一篇: 极简实用的Asp.NetCore模块化框