歡迎光臨
每天分享高質量文章

.NET Core 3.0之深入原始碼理解Configuration(一)

微軟在.NET Core里設計出了全新的配置體系,並以非常靈活、可擴展的方式實現。從其原始碼來看,其運行機制大致是,根據其Source,創建一個Builder實體,並會向其添加Provider,在我們使用配置信息的時候,會從記憶體中獲取相應的Provider實體。

.NET Core採用了統一的呼叫方式來加載不同型別的配置信息,並通過統一的抽象接口IConfigurationSource對配置源進行管理,這也是剛剛所說的靈活。而其擴展性就是我們可以自己自定義新的Provider實體,而不會改變其原來的呼叫方式。接下來的文章將會基於Consul,擴展一個新的Provider實體。

在ASP.NET Core 中,我們的應用配置是基於IConfigurationProvider的鍵值對。 我們先看一下思維導圖:

基於上圖,我們可以看到主要有鍵值對有多種,分別是:

 環境變數

命令列引數

各種形式的配置檔案

記憶體物件

用戶自定義擴展源

在介紹.NET Core配置功能之前,先簡要說明一下Microsoft.Extensions.Configuration.Abstractions,該組件抽象了.NET Core的配置功能,並對自定義擴展制定了新的標準。以下介紹的四個核心物件全部來自於該組件。

IConfiguration

該接口表示一組鍵/值應用程式配置屬性,應用程式使用配置時的入口物件,.NET Core對其有多種擴展,其派生類包括位於統一類庫的IConfigurationSection,以及Microsoft.Extensions.Configuration類庫中的ConfigurationRoot、ConfigurationSection、IConfigurationRoot。我們可以通過DI獲取IConfiguration實體。

它主要有以下三個方法:

  • GetChildren():獲取直接子配置子節
  • GetReloadToken():傳回一個IChangeToken,可用於確定何時重新加載配置
  • GetSection(String):獲取指定鍵的子節點

我們來看一下原始碼:

通常我們要求配置檔案要有足夠的靈活性,尤其是我們所擴展的配置信息存放在了其他服務器,當修改的時候我們很需要一套監控功能,以及時靈活的應對配置信息的修改。現在.NET Core為我們提供了這樣一個功能,我們只需要自定義少量代碼即可完成配置信息的同步。這個方法就是GetReloadToken(),其傳回值是IChangeToken。此處對配置信息的同步只做一個引子,後面的文章會詳細說明。

由於ConfigurationRoot、ConfigurationSection聚集於IConfiguration接口,此處也對這兩個類進行討論,方便我們對.NET Core的配置功能有個更加形象的印象。這兩個接口,本質上就是.NET Core關於配置信息的讀取方式。

XML是使用比較廣泛的一種資料結構,我們在配置XML時,一般會使用根節點、父節點、子節點之類的術語,此處也一樣。

ConfigurationRoot是配置的根節點,也實現了IConfigurationRoot,此接口只有一個方法,其主要功能就是實現對配置信息的重新加載,另外還包括一個IConfigurationProvider型別的集合屬性。其原始碼如下

通過原始碼我們知道,如果呼叫了Reload()方法,所有型別的Provider都會重新加載。

前面有ConfigurationRoot表示配置的根節點,那麼ConfigurationSection則表示非跟節點,畢竟父節點、子節點都是相對,所以此處使用非根節點。ConfigurationSection繼承於IConfigurationSection,該接口只有三個只讀屬性,分別表示配置信息的Key、Value以及路徑信息,需要指出的是,此處的路徑信息主要指從根節點到當前節點的路徑,以表示當前節點的位置,類似於A:B:C可以表示節點C的位置,其中A、B、C都是ConfigurationSection的Key。以下是ConfigurationSection的原始碼

IConfigurationBuilder

該接口主要用於創建IConfigurationProvider,其派生類包括Microsoft.Extensions.Configuration.ConfigurationBuilder。其成員包括

兩個只讀屬性:

  • Properties:獲取可用於在IConfigurationBuilder之間共享資料的鍵/值集合
  • Sources:該屬性用於快取不同的配置源,以用於相對應的Provider的創建

兩個方法:

  • Add(IConfigurationSource source):新增IConfigurationSource,並添加到屬性中Sources中
  • Build():該方法遍歷Sources屬性,並呼叫IConfigurationSource的Build()方法,通過獲取Provider集合,最終創建IConfigurationRoot物件

ConfigurationBuilder原始碼如下

此處令人感慨頗多,我們最終呼叫 ConfigurationRoot 的建構式,究其原因是Provider提供了統一的資料訪問方式,不管是基於何種型別的Provider,我們都可以呼叫其Load()方法加載配置項。此外,IConfigurationBuilder本身有很多的擴展方法來註冊資料源,比如AddJsonFile()擴展方法。我們來看一下,我們常見的寫法,


IConfigurationSource

該接口表示應用程式配置的鍵值對。其派生類包括Microsoft.Extensions.Configuration.ChainedConfigurationSource、Microsoft.Extensions.Configuration.Memory.MemoryConfigurationSource。另外該派生類還會在檔案類配置場景下依賴Microsoft.Extensions.Configuration.FileExtensions組件。

它是所有配置源的抽象表示,包括JSON、XML、INI、環境變數等等。通過上文我們也知道了,IConfigurationBuilder會註冊多個IConfigurationSource實體。它只有一個方法,就是Build()方法,並傳回IConfigurationProvider,由此可見,IConfigurationProvider的創建依賴於IConfigurationSource,這也是一一對應的關係。所有不同的源最終都會轉化成統一的鍵值對錶示。

以下是MemoryConfigurationSource的原始碼


IConfigurationProvider

通過上文的介紹,我們可以知道IConfigurationProvider是統一的對外接口,對用戶提供配置的查詢、重新加載等功能。其派生類包括Microsoft.Extensions.Configuration.ConfigurationProvider、Microsoft.Extensions.Configuration.ChainedConfigurationProvider、Microsoft.Extensions.Configuration.Memory.MemoryConfigurationProvider。另外該派生類還會在檔案類配置場景下依賴Microsoft.Extensions.Configuration.FileExtensions組件。

以下是Microsoft.Extensions.Configuration.ConfigurationProvider的原始碼:


通過原始碼,我們可以知道ConfigurationProvider以字典型別快取了多個Provider物件,有需要的時候,從記憶體中獲取即可,配置的加載通過Load()方法實現,在ConfigurationRoot里我們介紹了其Reload,並且說明其方法是在迴圈呼叫ConfigurationProvider的Load方法,但是此處只提供了一個虛方法,其目的是要交給其他具體的Provider,比如環境變數、JSON、XML等,這些具體的Provider可以從相應的配置源中獲取配置信息。所有的子節點KEY通過GetChildKeys方法實現,其重新加載方式通過ConfigurationReloadToken實體完成。

另外需要說明一下,在ConfigurationProvider建構式里,對字典進行了初始化,並同時設置了字典Key不受大小寫限制,這是一個需要註意的細節。

通過查看.NET配置功能的原始碼,所有依賴均基於Microsoft.Extensions.Configuration.Abstractions,在其上有一層實現,即Microsoft.Extensions.Configuration,其內部也多數是抽象實現,並提供了多個虛方法交給其派生組件,比如環境變數、命令列引數、各種檔案型配置等,當然各種檔案型配置還要依賴Microsoft.Extensions.Configuration.FileExtensions組件。

以下是.NET Core 3.0預覽版里的Configuration各個組件的結構圖:

赞(0)

分享創造快樂