.NET Core實戰專案之CMS 第三章 入門篇-原始碼解析配置檔案及依賴注入
作者:依樂祝
原文連結:https://www.cnblogs.com/yilezhu/p/9998021.html
寫在前面
上篇文章我給大家講解了ASP.NET Core的概念及為什麼使用它,接著帶著你一步一步的配置了.NET Core的開發環境並建立了一個ASP.NET Core的mvc專案,同時又通過一個實戰教你如何在頁面顯示一個Content的列表。不知道你有沒有跟著敲下程式碼,千萬不要做眼高手低的人哦。這篇文章我們就會設計一些複雜的概念了,因為要對ASP.NET Core的啟動及執行原理、配置檔案的家在過程進行分析,依賴注入,控制反轉等概念的講解等。俗話說,授人以魚不如授人以漁,所以文章旨在帶著大家分析原始碼,讓大家能知其然更能知其所以然。為了偷懶,繼續使用上篇文章的例子了!有興趣的朋友可以加群637326624相互交流!
ASP.NET Core啟動原始碼解析
這部分我就帶著大家一起看下asp.net core專案的執行流程吧!順帶著瞭解下asp.net core的執行原理,說的不好的話,希望大家給以指正,從而能夠正確的幫助更多的人。
首先上一下上篇文章的專案結構吧,如下所示,熟悉C#的朋友應該知道,要找程式的入庫,那麼就應該找到Main方法。而asp.net core的main方法就在Program.cs檔案中。
開啟後看到如下的程式碼,我加了註釋,大夥將就看下,下面我們來一步一步的分析
/// <summary> /// Main方法,程式的入口方法 /// </summary> /// <param name="args"></param> public static void Main(string[] args) { CreateWebHostBuilder(args)//呼叫下面的方法,返回一個IWebHostBuilder物件 .Build()//用上面返回的IWebHostBuilder物件建立一個IWebHost .Run();//執行上面建立的IWebHost物件從而執行我們的Web應用程式換句話說就是啟動一個一直執行監聽http請求的任務 } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args)//使用預設的配置資訊來初始化一個新的IWebHostBuilder例項 .UseStartup<Startup>();// 為Web Host指定了Startup類
可以看到asp.net core程式實際上就是一個控制檯程式,執行一個webhost物件從而啟動一個一直執行的監聽http請求的任務。所以我們的重點就是分析一下這個WebHost建立的過程:
建立IWebHostBuilder-》建立IWebHost-》然後執行建立的IWebHost。這裡我們從IWebHostBuilder的Build分析下建立的過程,有興趣的朋友可以看下,沒興趣的朋友可以直接跳到下一個步驟繼續閱讀。
首先到aspnetcore的github開源地址https://github.com/aspnet/AspNetCore/tree/release/2.1 上去下載原始碼(我們使用的是2.1)。然後使用vscode開啟解壓後的資料夾。至於vscode如何載入檔案,你可以看我這篇文章
根據IWebHostBuilder的名稱空間我們找到了它的實現,路徑為src/Hosting/Hosting/src/WebHostBuilder.cs
通過上面的程式碼我們可以看到首先是通過BuildCommonServices來構建一個ServiceCollection。為什麼說這麼說呢,先讓我們我們跳轉到BuidCommonServices方法中看下吧。
可以看到,
var services = new ServiceCollection();
首先new一個ServiceCollection然後往services裡面注入很多內容,比如:WebHostOptions ,IHostingEnvironment ,IHttpContextFactory ,IMiddlewareFactory 等等(其實這裡已經設計到依賴注入的概念了,先思考下吧),然後我們在後續就可以使用了!最後這個BuildCommonServices就返回了這個services物件。在上面的依賴注入中有一個方法,不知道大家注意到沒有,因為我們在步驟2貼出的程式碼裡面有一個
UseStartup<Startup>()
其實在上面的BuildCommonServices方法中也有對IStartup
的注入的。首先,判斷Startup類是否繼承於IStartup介面,如果是繼承的,那麼就可以直接加入在services 裡面去,如果不是繼承的話,就需要通過ConventionBasedStartup(methods)把method轉換成IStartUp後注入到services裡面去。結合上面我們的程式碼,貌似我們平時用的時候注入的方式都是採用後者。我們再回到build方法拿到了BuildCommonServices方法構建的ServiceCollection例項後,通過GetProviderFromFactory(hostingServices) 方法構造出了IServiceProvider 物件。到目前為止,IServiceCollection和IServiceProvider都拿到了。然後根據IServiceCollection和IServiceProvider物件構建WebHost物件。構造了WebHost例項還不能直接返回,還需要通過Initialize對WebHost例項進行初始化操作。那我們看看在初始化函式Initialize中,都做了什麼事情吧。
這裡我們把程式碼導航到src/Hosting/Hosting/src/Internal/WebHost.cs找到Initialize方法。如下圖所示:主要就是一個EnsureApplicationServices 方法。
我們繼續導航檢視這個方法的內容如下:就是拿到Startup 物件,然後把_applicationServiceCollection 中的物件注入進去。
至此我們build中註冊的物件以及StartUp中註冊的物件都已經加入到依賴注入容器中了,接下來就是Run起來了。這個run的程式碼在src\Hosting\Hosting\src\WebHostExtensions.cs中,程式碼如下:WebHost執行RunAsync執行web應用程式並返回一個只有在觸發或關閉令牌時才完成的任務 。這就是我們執行ASP.Net Core程式的時候,看到的那個命令列視窗了,如果不關閉視窗或者按Ctrl+C的話是無法結束的。
至此啟動的過程的原始碼分析完成了。
配置檔案
上面給大家介紹了ASP.NET Core的啟動過程,中間牽扯到了一些依賴注入的概念。關於依賴注入的概念呢,我們後面再說,這裡先給大家講解下配置檔案的載入過程。開啟上篇文章我們建立的專案,並在appsettings.json裡面加入如下內容:
{ "Logging": { "LogLevel": { "Default": "Warning" } }, "Content": { "Id": 1, "title": "title1", "content": "content1", "status": 1, "add_time": "2018-11-21 16:29", "modify_time": null }, "AllowedHosts": "*" }
然後在Startup類中ConfigureServices中註冊TOptions物件如下所示:
services.Configure<Content>(Configuration.GetSection("Content"));//註冊TOption例項物件
這段程式碼也就是從appsettings.json這個配置檔案中的
Content
這個節點匹配到Content這個物件上。修改下ContentController這個控制器程式碼如下:
private readonly Content contents; public ContentController(IOptions<Content> option) { contents = option.Value; } /// <summary> /// 首頁顯示 /// </summary> /// <returns></returns> public IActionResult Index() { return View(new ContentViewModel { Contents=new List<Content> { contents} }); }
按下F5執行下,然後導航到Content目錄看到如下頁面:說明成功從appsettings.json這個檔案中載入了內容。這一切是怎麼發生的呢?下面我們就一步一步的來分析。
我們回過頭來看我們的Main方法,發現裡面有一個CreateDefaultBuilder方法,就是這個方法裡面為我們做了一些預設的設定,然後載入我們的配置檔案的!
我們在原始碼裡面找到CreateDefaultBuilder 的原始碼(反正我找了半天,起初在Hosting下面找,實際上在MetaPackages下面的),位置在src\MetaPackages\src\Microsoft.AspNetCore\WebHost.cs 有的人可能找不到哦,可以看到這個方法會在ConfigureAppConfiguration 的時候預設載入
appsetting
檔案,並做一些初始的設定,所以我們不需要任何操作,就能載入appsettings
的內容了。既然知道了原理後,我們就試著重寫下這個
ConfigureAppConfiguration
然後載入我們自定義的json檔案吧。滑鼠右鍵新建一個Content.json檔案,然後輸入如下的內容:
{ "ContentList": { "Id": 1, "title": "title1 from diy json", "content": "content1 from diy json", "status": 1, "add_time": "2018-11-21 16:29", "modify_time": null } }
然後開啟Program.cs。按如下程式碼進行改造:
/// <summary> /// Main方法,程式的入口方法 /// </summary> /// <param name="args"></param> public static void Main(string[] args) { CreateWebHostBuilder(args)//呼叫下面的方法,返回一個WebHostBuilder物件 .Build()//用上面返回的WebHostBuilder物件建立一個WebHost .Run();//執行上面建立的WebHost物件從而執行我們的Web應用程式換句話說就是啟動一個一直執行監聽http請求的任務 } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args)//使用預設的配置資訊來初始化一個新的IWebHostBuilder例項 .ConfigureAppConfiguration((hostingContext, config) => { var env = hostingContext.HostingEnvironment; config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true) .AddJsonFile("Content.json",optional:false,reloadOnChange:false) .AddEnvironmentVariables(); }) .UseStartup<Startup>();// 為Web Host指定了Startup類
然後Startup裡面ConfigureServices中的程式碼修改如下:
然後按下F5執行下程式碼吧,如下圖所示,從我們最新新增的json檔案中加載出來資料了。
這裡多講一點,傳統asp.net的web.config檔案如果有更改的話是必須要重啟站點才能使,配置檔案生效的,但是asp.net core的配置檔案是支援熱更新的,及不重啟網站也能載入更新,只需要設定一下屬性即可,如下圖所示:
配置檔案的原始碼解讀這塊就到這裡了。下面開始依賴注入的講解。
依賴注入與控制反轉
如果大家仔細閱讀文章的話,相信已經看出來了,我上面提到過好幾次依賴注入的概念。那麼究竟什麼是依賴注入呢?下面我們就拿我們上面的ContentController來好好的來理解下。
依賴注入:當一個物件ContentController需要另一個物件Content來協同完成任務的時候,那麼這個ContentController就對這個Content物件產生了依賴關係。那麼在這個ContentController中,是怎麼注入的呢?就是從控制器中注入的了,如下圖所示:
從asp.net 轉過來的你是不是想起了之前的千篇一律的new物件啊。沒物件自己new(要是女朋友也能new多好啊……)當然除了單例物件,靜態哈。
這裡又設計一個概念就是控制反轉。
那麼什麼是控制反轉呢?你上面看到沒有,你自己new物件就是整轉,因為你自己建立自己所要使用的物件,。那麼這種不需要你自己new物件,而是直接傳進來就是控制反轉了。(不知道比喻的恰不恰當哈)
依賴注入與控制反轉你是否已經瞭解了呢,喜歡思考的朋友可能會問了,那這個建構函式裡面的IOptions<Content> option
又是怎麼出來的?這裡就要引入一個容器的概念了。
什麼是容器呢?
這裡建立IOptions<Content> option
這個物件的東西就是容器。還記得上面我們分析原始碼的時候,IServiceCollection 裡面注入了很多東西嗎?其實就是往IServiceCollection 這個容器裡面注入方法,這樣其他地方使用的時候就能自動注入了。
這就是容器的好處,由容器來統一管理例項的建立和銷燬,你只需要關心怎麼用就行了,不需要關係怎麼建立跟銷燬。
當然容器建立的例項都是有生命週期的,。下面羅列一下,就不過多的講解了。
- Transient: 每一次訪問都會建立一個新的例項
- Scoped: 在同一個Scope內只初始化一個例項 ,可以理解為( 每一個request級別只建立一個例項,同一個http request會在一個 scope內)
- Singleton :整個應用程式生命週期以內只建立一個例項
使用的方式也很簡單,我會在接下來的課程中詳細的通過例項來進行講解!因為現在的例子還沒發演示。
總結
本文一步一步帶著你先分析了ASP.NET Core的啟動過程及執行的原理,緊接著給你講了配置檔案的載入過程及原理,並通過示例程式碼演示瞭如何載入自定義的配置檔案,最後引出了依賴注入以及控制反轉的概念,並通過對我們上面例子的分析來緊身對依賴注入以及控制反轉的理解。至此讓你知其然更知其所以然。對ASP.NET Core的原理相信你已經瞭然於胸了!有問題的小夥伴可以加群637326624
討論。那麼接下來讓我們再準備下dapper,vue以及git的快速入門就開始我們的asp.net core cms的實戰課程吧!還是那句話基礎很重要,基礎打好,後面才能事半功倍。謝謝大家。