zoukankan      html  css  js  c++  java
  • .NET Core 3.0之深入源码理解Configuration(二)

     

    文件型配置基本内容

    上一篇文章讨论了Configuration的几个核心对象,本文继续讨论Configuration中关于文件型配置的相关内容。相比较而言,文件型配置的使用场景更加广泛,用户自定义配置扩展也可以基于文件型配置进行扩展。如果需要查看上一篇文章,可以点击移步

    .NET Core文件型配置中我们提供了三种主要的实现,分别是JSON、XML、INI,请查看下图

    file2

    由图可知,这三种配置的实现方式是一样的,当然了其他的配置比如命令行配置、环境变量配置等也是大同小异,理解了改配置类型的实现方式,后面我们再扩展基于Consul或者ZK的实现,就非常简单了。

    文件型配置的抽象扩展

    文件型配置的抽象扩展位于Microsoft.Extensions.Configuration.FileExtensions组件中,该扩展是一个基础实现。不过其命名空间是Microsoft.Extensions.Configuration,而Micros oft.Extensions.Configuration扩建本身又是整个.NET Core Configuration的基础实现。将File扩展独立于外部,体验了.NET Core的模块化设计。

    FileConfigurationSource

    Configuration.FileExtensions组件中,FileConfigurationSource是继承于IConfigurationSource的一个抽象类,包含了一个IConfigurationProvider类型的抽象方法,如下所示

       1:  /// <summary>
       2:  /// Builds the <see cref="IConfigurationProvider"/> for this source.
       3:  /// </summary>
       4:  /// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
       5:  /// <returns>A <see cref="IConfigurationProvider"/></returns>
       6:  public abstract IConfigurationProvider Build(IConfigurationBuilder builder);

    该抽象类中还包括了几个比较重要的参数,分别用于配置性行为、文件内容访问以及异常处理。

    string Path:文件的路径

    bool Optional:标识加载的文件是否是可选的

    bool ReloadOnChange:如果文件发生修改,是否重新加载配置源

    int ReloadDelay:加载延迟,单位是毫秒,默认是250毫秒

    IFileProvider FileProvider:用于获取文件内容

    Action<FileLoadExceptionContext> OnLoadException:文件加载异常处理

    该类对FileProvider有特殊处理,就是如果没有提供FileProvider实例,则会基于绝对路径,在最近的现有目录中创建物理文件提供程序。源码如下,

       1:  /// <summary>
       2:  /// If no file provider has been set, for absolute Path, this will creates a physical file provider 
       3:  /// for the nearest existing directory.
       4:  /// </summary>
       5:  public void ResolveFileProvider()
       6:  {
       7:      if (FileProvider == null && 
       8:          !string.IsNullOrEmpty(Path) &&
       9:          System.IO.Path.IsPathRooted(Path))
      10:      {
      11:      
    var directory = System.IO.Path.GetDirectoryName(Path);
      12:          var pathToFile = System.IO.Path.GetFileName(Path);
      13:          while (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
      14:          {
      15:              pathToFile = System.IO.Path.Combine(System.IO.Path.GetFileName(directory), pathToFile);
      16:              directory = System.IO.Path.GetDirectoryName(directory);
      17:          }
      18:          if (Directory.Exists(directory))
      19:          {
      20:              FileProvider = new PhysicalFileProvider(directory);
      21:              Path = pathToFile;
      22:          }
      23:      }
      24:  }

    FileConfigurationProvider

    该类是继承于ConfigurationProvider的抽象类,是从文件系统加载配置的基类,同时还继承了IDisposable,其抽象方法是Load方法,用于从当前的Provider中以Stream方式加载数据

       1:  /// <summary>
       2:  /// Loads this provider's data from a stream.
       3:  /// </summary>
       4:  /// <param name="stream">The stream to read.</param>
       5:  public abstract void Load(Stream stream);

    该类还重写了ConfigurationProvider的Load方法,并对文件加载中的异常做了处理,Data属性在前文有提到过,此处不再做其他说明。方法源码如下所示:

       1:  private void Load(bool reload)
       2:  {
       3:      var file = Source.FileProvider?.GetFileInfo(Source.Path);
       4:      if (file == null || !file.Exists)
       5:      {
       6:          if (Source.Optional || reload) // Always optional on reload
       7:          {
       8:              Data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
       9:          }
      10:          else
      11:          {
      12:              var error = new StringBuilder($"The configuration file '{Source.Path}' was not found and is not optional.");
      13:              if (!string.IsNullOrEmpty(file?.PhysicalPath))
      14:              {
      15:                  error.Append($" The physical path is '{file.PhysicalPath}'.");
      16:              }
      17:              HandleException(new FileNotFoundException(error.ToString()));
      18:          }
      19:      }
      20:      else
      21:      {
      22:          // Always create new Data on reload to drop old keys
      23:          if (reload)
      24:          {
      25:              Data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
      26:          }
      27:          using (var stream = file.CreateReadStream())
      28:          {
      29:              try
      30:              {
      31:                  Load(stream);
      32:              }
      33:              catch (Exception e)
      34:              {
      35:                  HandleException(e);
      36:              }
      37:          }
      38:      }
      39:      // REVIEW: Should we raise this in the base as well / instead?,通过注释,我们可以知道OnReload()方法可能会在新版中发生变化
      40:      OnReload();
      41:  }
      42:   
      43:  /// <summary>
      44:  /// Loads the contents of the file at <see cref="Path"/>.
      45:  /// </summary>
      46:  /// <exception cref="FileNotFoundException">If Optional is <c>false</c> on the source and a
      47:  /// file does not exist at specified Path.</exception>
      48:  public override void Load()
      49:  {
      50:      Load(reload: false);
      51:  }

    另外它还有一个特殊方法,就是参数类型为FileConfigurationSource的构造函数,其主要功能是监控文件,并在FileConfigurationSource.ReloadDelay设置的时间里重新加载文件并返回一个IDisposable类型的值,以下是该构造函数的源码:

       1:  /// <summary>
       2:  /// Initializes a new instance with the specified source.
       3:  /// </summary>
       4:  /// <param name="source">The source settings.</param>
       5:  public FileConfigurationProvider(FileConfigurationSource source)
       6:  {
       7:      if (source == null)
       8:      {
       9:          throw new ArgumentNullException(nameof(source));
      10:      }
      11:      Source = source;
      12:   
      13:      if (Source.ReloadOnChange && Source.FileProvider != null)
      14:      {
      15:          _changeTokenRegistration = ChangeToken.OnChange(
      16:              () => Source.FileProvider.Watch(Source.Path),
      17:              () => {
      18:                  Thread.Sleep(Source.ReloadDelay);
      19:                  Load(reload: true);
      20:              });
      21:      }
      22:  }

    FileConfigurationExtensions

    该类是一个静态类,其提供了的多个扩展方法,主要基于

    • IConfigurationBuilder
    • IFileProvider
    • Action<FileLoadExceptionContext>

    包括主要用于设置或获取IFileProvider对象,前文有介绍过,是存储于字典之中,需要注意的是,在Get的时候如果字典中并不存在IFileProvider对象,则会实例化一个PhysicalFileProvider对象出来,该类位于Microsoft.Extensions.FileProviders.PhysicalFileProvider

       1:  /// <summary>
       2:  /// Sets the default <see cref="IFileProvider"/> to be used for file-based providers.
       3:  /// </summary>
       4:  /// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
       5:  /// <param name="fileProvider">The default file provider instance.</param>
       6:  /// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
       7:  public static IConfigurationBuilder SetFileProvider(this IConfigurationBuilder builder, IFileProvider fileProvider)
       8:  {
       9:      if (builder == null)
      10:      {
      11:          throw new ArgumentNullException(nameof(builder));
      12:      }
      13:   
      14:      builder.Properties[FileProviderKey] = fileProvider ?? throw new ArgumentNullException(nameof(fileProvider));
      15:      return builder;
      16:  }
      17:   
      18:  /// <summary>
      19:  /// Gets the default <see cref="IFileProvider"/> to be used for file-based providers.
      20:  /// </summary>
      21:  /// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
      22:  /// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
      23:  public static IFileProvider GetFileProvider(this IConfigurationBuilder builder)
      24:  {
      25:      if (builder == null)
      26:      {
      27:          throw new ArgumentNullException(nameof(builder));
      28:      }
      29:   
      30:      if (builder.Properties.TryGetValue(FileProviderKey, out object provider))
      31:      {
      32:          return provider as IFileProvider;
      33:      }
      34:   
      35:      return new PhysicalFileProvider(AppContext.BaseDirectory ?? string.Empty);
      36:  }

    为指定路径的物理文件设置文件型Provider,该方法同样基于PhysicalFileProvider,并返回IConfigurationBuilder对象

       1:  /// <summary>
       2:  /// Sets the FileProvider for file-based providers to a PhysicalFileProvider with the base path.
       3:  /// </summary>
       4:  /// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
       5:  /// <param name="basePath">The absolute path of file-based providers.</param>
       6:  /// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
       7:  public static IConfigurationBuilder SetBasePath(this IConfigurationBuilder builder, string basePath)
       8:  {
       9:      if (builder == null)
      10:      {
      11:          throw new ArgumentNullException(nameof(builder));
      12:      }
      13:   
      14:      if (basePath == null)
      15:      {
      16:          throw new ArgumentNullException(nameof(basePath));
      17:      }
      18:   
      19:      return builder.SetFileProvider(new PhysicalFileProvider(basePath));
      20:  }

    以及异常处理,可以看到其异常处理也会存放于字典中,如果字典中找不到,就会返回空,这个地方如果直接使用,需要注意空指针问题。

       1:  /// <summary>
       2:  /// Sets a default action to be invoked for file-based providers when an error occurs.
       3:  /// </summary>
       4:  /// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
       5:  /// <param name="handler">The Action to be invoked on a file load exception.</param>
       6:  /// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
       7:  public static IConfigurationBuilder SetFileLoadExceptionHandler(this IConfigurationBuilder builder, Action<FileLoadExceptionContext> handler)
       8:  {
       9:      if (builder == null)
      10:      {
      11:          throw new ArgumentNullException(nameof(builder));
      12:      }
      13:   
      14:      builder.Properties[FileLoadExceptionHandlerKey] = handler;
      15:      return builder;
      16:  }
      17:   
      18:  /// <summary>
      19:  /// Gets the default <see cref="IFileProvider"/> to be used for file-based providers.
      20:  /// </summary>
      21:  /// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
      22:  /// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
      23:  public static Action<FileLoadExceptionContext> GetFileLoadExceptionHandler(this IConfigurationBuilder builder)
      24:  {
      25:      if (builder == null)
      26:      {
      27:          throw new ArgumentNullException(nameof(builder));
      28:      }
      29:   
      30:      if (builder.Properties.TryGetValue(FileLoadExceptionHandlerKey, out object handler))
      31:      {
      32:          return handler as Action<FileLoadExceptionContext>;
      33:      }
      34:      
    return null;
      35:  }

    该类还有两个静态私有变量,指定了文件Provider的Key以及文件加载异常处理Key。

       1:  private static string FileProviderKey = "FileProvider";
       2:  private static string FileLoadExceptionHandlerKey = "FileLoadExceptionHandler";

    总结

    文件型配置还依赖于.NET Core的其他组件Microsoft.Extensions.FileProviders和Microsoft.Extensions.Primitives。

    FileProviders组件提供了文件处理的一般方法,Primitives组件提供了监控机制,同时还包括两个比较重要的结构体StringValues和StringSegment,本文暂时不做讨论,有兴趣的朋友,可以自行查看该组件源码。

  • 相关阅读:
    Mysql加锁过程详解(1)-基本知识
    Mysql加锁过程详解(5)-innodb 多版本并发控制原理详解
    java笔试题-1
    通过六个题目彻底掌握String笔试面试题
    JDBC实现往MySQL插入百万级数据
    打印变量地址-0x%08x
    cin中的注意事项
    猎豹网校C++ Primer学习笔记
    物体检测相关——学习笔记
    teraflop级、TFLOPS、TOPS
  • 原文地址:https://www.cnblogs.com/edison0621/p/10889325.html
Copyright © 2011-2022 走看看