写在前面

上一篇文章讨论了文件型配置的基本内容,本篇内容讨论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对外使用的时候,都是以字典方式去提供给外界使用的。

那么,这个类是如何将数据流转化为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的实现,我们完全可以把这个类当成工具类去使用。

.NET Core 3.0之深入源码理解Configuration(三)的更多相关文章

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

    Configuration总体介绍 微软在.NET Core里设计出了全新的配置体系,并以非常灵活.可扩展的方式实现.从其源码来看,其运行机制大致是,根据其Source,创建一个Builder实例,并 ...

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

      文件型配置基本内容 上一篇文章讨论了Configuration的几个核心对象,本文继续讨论Configuration中关于文件型配置的相关内容.相比较而言,文件型配置的使用场景更加广泛,用户自定义 ...

  3. .NET Core 3.0之深入源码理解Startup的注册及运行

    原文:.NET Core 3.0之深入源码理解Startup的注册及运行   写在前面 开发.NET Core应用,直接映入眼帘的就是Startup类和Program类,它们是.NET Core应用程 ...

  4. .NET Core 3.0之深入源码理解Kestrel的集成与应用(一)

      写在前面 ASP.NET Core 的 Web 服务器默认采用Kestrel,这是一个基于libuv(一个跨平台的基于Node.js异步I/O库)的跨平台.轻量级的Web服务器. 在开始之前,先回 ...

  5. .NET Core 3.0之深入源码理解Kestrel的集成与应用(二)

      前言 前一篇文章主要介绍了.NET Core继承Kestrel的目的.运行方式以及相关的使用,接下来将进一步从源码角度探讨.NET Core 3.0中关于Kestrel的其他内容,该部分内容,我们 ...

  6. .NET Core 3.0之深入源码理解HttpClientFactory(二)

      写在前面 上一篇文章讨论了通过在ConfigureServices中调用services.AddHttpClient()方法,并基于此进一步探讨了DefaultHttpClientFactory是 ...

  7. .NET Core 3.0之深入源码理解Host(二)

      写在前面 停了近一个月的技术博客,随着正式脱离996的魔窟,接下来也正式恢复了.本文从源码角度进一步讨论.NET Core 3.0 中关于Host扩展的一些技术点,主要讨论Long Run Pro ...

  8. .NET Core 3.0之深入源码理解ObjectPool(一)

    写在前面 对象池是一种比较常用的提高系统性能的软件设计模式,它维护了一系列相关对象列表的容器对象,这些对象可以随时重复使用,对象池节省了频繁创建对象的开销. 它使用取用/归还的操作模式,并重复执行这些 ...

  9. .NET Core 3.0之深入源码理解HealthCheck(一)

    写在前面 我们的系统可能因为正在部署.服务异常终止或者其他问题导致系统处于非健康状态,这个时候我们需要知道系统的健康状况,而健康检查可以帮助我们快速确定系统是否处于正常状态.一般情况下,我们会提供公开 ...

随机推荐

  1. RDLC报表 报表数据 栏 快捷键

    血淋淋的教训啊,这段时间用RDLC报表,创建报表后会在右边加载[服务器资源管理器][工具栏][报表数据]一些栏目 如图 [服务器资源管理器][工具栏]还好[报表数据]栏在开发的时候不小心点了X给关掉了 ...

  2. C# 打开指定的目录 记住路径中 / 与 \ 的使用方法

    老生常谈的问题了,C#在指定目录时,路径中要使用 \\.直接看实例 using System; namespace OpenFile{ class OpenFile{ static void Main ...

  3. PowerDesigner逆向工程,从SQL Server数据库生成Physical Model -----数据源方式

    1.File-Reverse Engineer-Database 2.DBMS选择SQL Server 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 一路Next..... ...

  4. 网络摄像机IPCamera RTSP直播播放网络/权限/音视频数据/花屏问题检测与分析助手EasyRTSPClient

    前言 最近在项目中遇到一个奇怪的问题,同样的SDK调用,访问海康摄像机的RTSP流,发保活OPTIONS命令保活,一个正常,而另一个一发就会被IPC断开,先看现场截图: 图1:发OPTIONS,摄像机 ...

  5. 九度OJ 1105:字符串的反码 (翻译)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:4929 解决:1529 题目描述: 一个二进制数,将其每一位取反,称之为这个数的反码.下面我们定义一个字符的反码.如果这是一个小写字符,则它 ...

  6. launchMode之的几种取值

    Activity的launchMode launchMode之standard   ·标准模式.每次激活Activity时均在当前任务栈中创建新的实例. 在配置文件里把activity节点的属性配置为 ...

  7. spring IOC(转)

    原文 http://stamen.iteye.com/blog/1489223 引述:IoC(控制反转:Inverse of Control)是Spring容器的内核,AOP.声明式事务等功能在此基础 ...

  8. 【C++基础学习】引用和指针、重载、函数、内存管理

    第一部分:引用VS指针 引用的含义:变量的别名 注意:变量不能只有别名,必须有一个真实的变量与之相对应 基本数据类型的引用 对别名本身的操作和它的实体的操作是一样的 1.基本数据类型的引用 类型 &a ...

  9. Android Touch事件分发

    跟touch事件相关的3个方法: public boolean dispatchTouchEvent(MotionEvent ev); //用来分派event public boolean onInt ...

  10. maven资料

    1.配置settings.xml:http://www.cnblogs.com/jingmoxukong/p/6050172.html?utm_source=gold_browser_extensio ...