.NET Core 3.0之深入源码理解Configuration(三)

发布时间:2019-05-19 22:58  浏览次数:25
 
写在前面

上一篇 <https://www.cnblogs.com/edison0621/p/10889325.html>
文章讨论了文件型配置的基本内容,本篇内容讨论JSON型配置的实现方式,理解了这一种配置类型的实现方式,那么其他类型的配置实现方式基本可以触类旁通。看过了上一篇
<https://www.cnblogs.com/edison0621/p/10889325.html>
文章的朋友,应该看得出来似曾相识。此图主要表达了文件型配置的实现,当然其他配置,包括自定义配置,都会按照这样的方式去实现。


<https://img2018.cnblogs.com/blog/533598/201905/533598-20190519225741492-2092328714.png>

JSON配置组件的相关内容

该组件有四个类

* JsonConfigurationExtensions
* JsonConfigurationSource
* JsonConfigurationFileParser
* JsonConfigurationProvider
这四个类相互合作,职责明确,共同将JSON类型的配置加载到内存中,供相应的系统使用。

JsonConfigurationFileParser

该类是一个内部类,拥有一个私有的构造方法,意味着该类无法在其他地方进行实例化,只能在自己内部使用。它只有一个公共方法,并且是静态的,如下所示
1: public static IDictionary<string, string> Parse(Stream input) => new
JsonConfigurationFileParser().ParseStream(input);

该方法通过读取输入数据流,将其转化为字典类型的配置数据,该字典类型是SortedDictionary类型的,并且不区分大小写,此处需要注意。这也呼应了之前所说的.NET
Core Configuration对外使用的时候,都是以字典方式去提供给外界使用的。

那么,这个类是如何将数据流转化为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类型存储的。

至此,JsonConfigurationFileParser完成了从文件读取内容并转化为键值对的工作。

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的实现,我们完全可以把这个类当成工具类去使用。

标签

归档

阅读排行