zoukankan      html  css  js  c++  java
  • 关于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())));
          }
        }
    }

    看到没,最后providers去调用了load方法。

    结语

    就上面的控制台代码来说IConfigurationSource对应的实现是JsonConfigurationSource;IConfigurationProvider,抽象类ConfigurationProvider对应的实现为JsonConfigurationProvider。如果我们要换成别的文件格式呢?比如ini,怎样自定义配置源呢?大家可以先想想,其实也很简单,下次跟大家分享。

    最后说真的,.netCore源码真的特别优秀,很值得花一番时间去看看!从其中可以学到许多架构知识!

    https://www.bilibili.com/video/BV1wg411T7Hi?spm_id_from=333.999.0.0

    https://www.bilibili.com/video/BV1QY411x7BC?spm_id_from=333.999.0.0

    作者:课间一起牛

    出处:https://www.cnblogs.com/mhg215/

    声援博主:如果您觉得文章对您有帮助,请点击文章末尾的【关注我】吧!

    别忘记点击文章右下角的【推荐】支持一波。~~~///(^v^)\~~~ .

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

    如果您有其他问题,也欢迎关注我下方的公众号,可以联系我一起交流切磋!

     B站: 课间一起牛的B站         知乎:课间一起牛的知乎

    码云:课间一起牛的码云      github:课间一起牛的github

  • 相关阅读:
    (转)typedef用法
    (转)在用户空间发生中断时,上下文切换的过程
    (转)内核中断,异常,抢占总结篇
    (转)中断上下文和进程上下文的区别
    (转)C中的volatile用法
    (转)gcc学习笔记
    (转)C系程序员面试必知必会之大端小端
    (转)我在北京工作这几年 – 一个软件工程师的反省
    (转)忠告
    Linux下VLAN功能的实现 (转)
  • 原文地址:https://www.cnblogs.com/mhg215/p/15514184.html
Copyright © 2011-2022 走看看