.NET Core 3.0之深刻源碼理解Configuration(三)

 

寫在前面

上一篇文章討論了文件型配置的基本內容,本篇內容討論JSON型配置的實現方式,理解了這一種配置類型的實現方式,那麼其餘類型的配置實現方式基本能夠舉一反三。看過了上一篇文章的朋友,應該看得出來似曾相識。此圖主要表達了文件型配置的實現,固然其餘配置,包括自定義配置,都會按照這樣的方式去實現。html

繪圖3

JSON配置組件的相關內容

該組件有四個類json

  • JsonConfigurationExtensions
  • JsonConfigurationSource
  • JsonConfigurationFileParser
  • JsonConfigurationProvider

這四個類相互合做,職責明確,共同將JSON類型的配置加載到內存中,供相應的系統使用。安全

JsonConfigurationFileParser

該類是一個內部類,擁有一個私有的構造方法,意味着該類沒法在其餘地方進行實例化,只能在本身內部使用。它只有一個公共方法,而且是靜態的,以下所示架構

   1:  public static IDictionary<string, string> Parse(Stream input) => new JsonConfigurationFileParser().ParseStream(input);

該方法經過讀取輸入數據流,將其轉化爲字典類型的配置數據,該字典類型是SortedDictionary類型的,而且不區分大小寫,此處須要注意。這也呼應了以前所說的.NET Core Configuration對外使用的時候,都是以字典方式去提供給外界使用的。ide

那麼,這個類是如何將數據流轉化爲JSON的呢,咱們繼續閱讀源碼函數

   1:  private IDictionary<string, string> ParseStream(Stream input)
   2:  {
   3:      _data.Clear();
   4:   
   5:      using (var reader = new StreamReader(input))
   6:      using (JsonDocument doc = JsonDocument.Parse(reader.ReadToEnd(), new JsonReaderOptions { CommentHandling = JsonCommentHandling.Skip }))
   7:      {
   8:          if (doc.RootElement.Type != JsonValueType.Object)
   9:          {
  10:              throw new FormatException(Resources.FormatError_UnsupportedJSONToken(doc.RootElement.Type));
  11:          }
  12:          VisitElement(doc.RootElement);
  13:      }
  14:   
  15:      return _data;
  16:  }

經過源碼,咱們知道,此處使用了JsonDocument處理StreamReader數據,JsonDocument又是什麼呢,經過命名空間咱們知道,它位於System.Text.Json中,這是.NET Core原生的處理JSON的組件,有興趣的朋友能夠去翻翻MSDN或者GitHub查找相關資料。此處不作過多說明。工具

VisitElement方法主要是遍歷JsonElement.EnumerateObject()方法中的對象集合,此處採用Stack<string>實例進行數據安全方面的控制。其中VisitValue是一個在處理json時至關全面的方法,說它全面是由於它考慮到了JSON值的幾乎全部類型:學習

  • JsonValueType.Object
  • JsonValueType.Array
  • JsonValueType.Number
  • JsonValueType.String
  • JsonValueType.True
  • JsonValueType.False
  • JsonValueType.Null

固然,該方法,並不會很傻的處理每一種類型,主要是針對Object和Array類型進行了遞歸遍歷,以便在諸如Number、String等的簡單類型時跳出遞歸,並存放到字典中,須要再次強調的是,存放在字典中的值是以String類型存儲的。ui

至此,JsonConfigurationFileParser完成了從文件讀取內容並轉化爲鍵值對的工做。this

JsonConfigurationSource

這個類比較簡單,由於繼承自FileConfigurationSource,如前文所說,FileConfigurationSource類已經作了初步的實現,只提供了一個Build方法交給子類去重寫。其返回值是JsonConfigurationProvider實例。

   1:  /// <summary>
   2:  /// Represents a JSON file as an <see cref="IConfigurationSource"/>.
   3:  /// </summary>
   4:  public class JsonConfigurationSource : FileConfigurationSource
   5:  {
   6:      /// <summary>
   7:      /// Builds the <see cref="JsonConfigurationProvider"/> for this source.
   8:      /// </summary>
   9:      /// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
  10:      /// <returns>A <see cref="JsonConfigurationProvider"/></returns>
  11:      public override IConfigurationProvider Build(IConfigurationBuilder builder)
  12:      {
  13:          EnsureDefaults(builder);
  14:          return new JsonConfigurationProvider(this);
  15:      }
  16:  }

此處的EnsureDefaults()方法,主要是設置FileProvider實例以及指定加載類型的異常處理方式。

JsonConfigurationProvider

這個類也很簡單,它繼承於FileConfigurationProvider,FileConfigurationProvider自己也已經通用功能進行了抽象實現,先看一下這個類的源碼

   1:  /// <summary>
   2:  /// A JSON file based <see cref="FileConfigurationProvider"/>.
   3:  /// </summary>
   4:  public class JsonConfigurationProvider : FileConfigurationProvider
   5:  {
   6:      /// <summary>
   7:      /// Initializes a new instance with the specified source.
   8:      /// </summary>
   9:      /// <param name="source">The source settings.</param>
  10:      public JsonConfigurationProvider(JsonConfigurationSource source) : base(source) { }
  11:   
  12:      /// <summary>
  13:      /// Loads the JSON data from a stream.
  14:      /// </summary>
  15:      /// <param name="stream">The stream to read.</param>
  16:      public override void Load(Stream stream)
  17:      {
  18:          try {
  19:              Data = JsonConfigurationFileParser.Parse(stream);
  20:          } catch (JsonReaderException e)
  21:          {
  22:              throw new FormatException(Resources.Error_JSONParseError, e);
  23:          }
  24:      }
  25:  }

其構造函數的傳入參數類型是JsonConfigurationSource,這和JsonConfigurationSource.Build()方法中的return new JsonConfigurationProvider(this)代碼片斷相呼應。

JsonConfigurationProvider所重寫的方法,調用的是JsonConfigurationFileParser.Parse(stream)方法,因此該類顯得很是的輕量。

JsonConfigurationExtensions

這個方法,你們就更熟悉了,咱們平時所使用的AddJsonFile()方法,就是在這個擴展類中進行擴展的,其返回值是IConfigurationBuilder類型,其核心方法源碼以下所示

   1:  /// <summary>
   2:  /// Adds a JSON configuration source to <paramref name="builder"/>.
   3:  /// </summary>
   4:  /// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
   5:  /// <param name="provider">The <see cref="IFileProvider"/> to use to access the file.</param>
   6:  /// <param name="path">Path relative to the base path stored in 
   7:  /// <see cref="IConfigurationBuilder.Properties"/> of <paramref name="builder"/>.</param>
   8:  /// <param name="optional">Whether the file is optional.</param>
   9:  /// <param name="reloadOnChange">Whether the configuration should be reloaded if the file changes.</param>
  10:  /// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
  11:  public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, IFileProvider provider, string path, bool optional, bool reloadOnChange)
  12:  {
  13:      if (builder == null)
  14:      {
  15:          throw new ArgumentNullException(nameof(builder));
  16:      }
  17:      if (string.IsNullOrEmpty(path))
  18:      {
  19:          throw new ArgumentException(Resources.Error_InvalidFilePath, nameof(path));
  20:      }
  21:   
  22:      return builder.AddJsonFile(s =>
  23:      {
  24:          s.FileProvider = provider;
  25:          s.Path = path;
  26:          s.Optional = optional;
  27:          s.ReloadOnChange = reloadOnChange;
  28:          s.ResolveFileProvider();
  29:      });
  30:  }

不過,你們不要看這個方法的代碼行數不少,就認爲,其餘方法都重載於該方法,其實該方法重載自

   1:  /// <summary>
   2:  /// Adds a JSON configuration source to <paramref name="builder"/>.
   3:  /// </summary>
   4:  /// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
   5:  /// <param name="configureSource">Configures the source.</param>
   6:  /// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
   7:  public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, Action<JsonConfigurationSource> configureSource)
   8:      => builder.Add(configureSource);

這個方法最終調用的仍是IConfigurationBuilder.Add()方法

總結

經過介紹以上JSON Configuration組件的四個類,咱們知道了,該組件針對JSON格式的文件的處理方式,不過因爲其實文件型配置,其抽象實現已經在文件型配置擴展實現。

從這裏,咱們能夠學習一下,若是有一天咱們須要擴展遠程配置,好比Consul、ZK等,咱們也能夠考慮並採用這種架構的設計方式。另外在JSON Configuration組件中,.NET Core將專有型功能方法的處理進行了聚合,並聚焦關注點的方式也值得咱們學習。

最後JsonConfigurationFileParser中給了咱們一種關於Stream轉換成JSON的實現,咱們徹底能夠把這個類當成工具類去使用。

相關文章
相關標籤/搜索