.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的实现,咱们彻底能够把这个类当成工具类去使用。

相关文章
相关标签/搜索