依賴注入
在asp.net core程式中,眾所周知,依賴注入基本上貫穿了整個專案,以通用的結構來講解,控制器層(Controller層)依賴業務層(Service層),業務層依賴於倉儲層(Repository層),而其他層級中也或多或少的使用了依賴注入,在這裡不過多的對於依賴注入概念上不進行講解,如果有不瞭解的同學,可以在微軟官網或者在搜尋引擎搜尋依賴注入相關概念,本文主要講解如何在asp.net core中實現自己的依賴注入容器,並且希望更多的同學能夠去閱讀原始碼碼,因為原始碼中暴露的一些抽象類或者介面向開發者提供了方便開發者自定義或者擴充的口子。好了,不多囉嗦,我們開始。
First IServiceProviderFactory介面
用過Autofac的同學都知道在asp.net core3版本之後,Autofac的使用方式稍微發生了一些變化,首先需要在Program.cs檔案中需要使用Host.UseServiceProviderFactory方法,那實際上這個方法就是讓開發者能夠去實現自定義的依賴注入容器的一個擴充方法,我們可以檢視一下這個方法的定義,注意看有個重點的介面就是IServiceProviderFactory介面,這個介面實際上是指定服務提供者的一個抽象工廠泛型介面,這是實現自定義IOC中最重要的介面之一,也是最開始的一步,實際上,實現自定義依賴注入容器,只需要實現兩個介面就可以實現自定義容器,可以看到這個方法有兩種引數機制一種是直接傳入對應的 實現類,另一種是使用委託的方式去建立物件,並且傳入了一個HostBuilderContext的物件,我們會使用這種方式去實現。
Second IServiceProvider介面
我們可以看到這個IServiceProviderFactory介面有兩個實現方法,一個是CreateBuilder方法,裡面傳入IServiceCollection變數,另一個方法是CreateServiceProvider方法,傳入我們這個介面指定的容器類的物件,其中IocContainer類不依賴於任何一個抽象,第一個方法的作用就是去構造這個容器的物件,需要返回我們指定的型別的物件,即是這個類是代表著容器,存放服務的,第二個方法是將上面構造的容器物件傳入進來,並且返回我們指定的服務提供者,那概念很清晰了,第一個IServiceProviderFactory介面是用來指定我們的容器是哪一個類是我們的容器,以及哪一個是我們的服務提供者,那實際上的IServiceProvider就是第二個重要的一個介面了,這個介面是隻有一個方法,GetService方法,引數是一個Type,代表著我們是要去獲取哪一個型別的引數,返回值是Object,返回下層依賴者所需要的具體的一個物件。
Three 遵循規則實現自定義容器
那實際上自帶的依賴注入容器也是遵循這種規則去實現的,它提供了一個自帶的一個ServiceProvider的類去建立物件,那大家都知道啟動一個Core的一個程式,自帶的一些依賴物件都有一百多個,那大家可能會覺得,讓自己去寫這種一百多個物件的建立,並且類別還是core的開發者所沒有暴露出的型別,建立起來會很麻煩,並且還存在各種依賴,讓大家覺得可能自定義依賴注入容器可能很難,實際上,剛開始的時候我也是這麼想的,表示式樹在我去年十二月份的時候就開始寫程式碼了,只是今年才上傳到部落格,那實際上,自定義容器我也是去年開始研究的,剛開始也是寫了很多判斷因為它內部啟動的時候大的依賴了兩個東西一個是配置的IConfiguration,還有一個就是一個Host的一個類,下面又依賴了很多很多的類,感覺建立起來很麻煩,後來在想到了反射是可以獲取程式執行時的後設資料並且去構造某個型別,那實際上,我們是可以用反射去實現一種投機取巧的方式去實現自定義依賴注入容器,那就是將啟動所依賴服務由自帶的ServiceProvider去進行提供和建立,一些專案開發中使用的服務由我們去進行管理,那說到這,已經有很多同學知道了怎麼去進行了,我們看程式碼。
上圖中,我們可以看到Provider類是實現了IServiceProvider的介面,並且實現了GetService的方法,可以看到,我使用的方式是去用反射去獲取自帶的ServiceProvider的建構函式,然後建立這個物件,並且在GetService方法中,首先去判斷能否從自帶的Provider去獲取和建立物件,如果獲取不到,那說明是我們專案中所需要的型別,從而使用我們自定義的容器去進行獲取物件,預設的獲取不到是因為我們在建立ServiceProvider物件的時候傳入了IServiceCollection的物件,這裡所包含的就是啟動Core程式所需要的依賴的集合,這樣我們就可以保證,程式啟動的時候是可以正常啟動的,然後在執行中,請求中所需要的服務型別是由我們自己去建立物件的,所以這樣就實現了簡單的IOC依賴注入容器,並且替換掉自帶的容器。
Four 控制器層的屬性注入以及擴充容器實現屬性注入和一介面多實現
按照我文章剛開始的時候所說,微軟給我們暴露了很多供我們自定義的介面和 抽象類,那如果需要在控制器層實現屬性注入那怎麼辦呢?那實際上還有一個介面,用來讓我們去建立控制器,那就是IControllerFactory介面,這個介面有兩個方法,一個是CreateController方法和ReleaseController方法,顧名思義就是一個是建立控制器,一個是銷燬控制器,那我們可以在第一個方法去實現控制器層的屬性注入以及一個介面多實現該怎麼去獲取的思路,如果是屬性注入,我們是需要去建立一個特性用來標記這個屬性是用來從容器中獲取物件的,我們可以在控制器層或者其他類中使用類似的方法去操作屬性注入賦值,如果是一個介面多實現呢,也是需要去定義兩個特性,一個特性標記在實現類上面,並且建構函式中有一個string型別的引數,用來標記是在容器中 注入的時候使用某個名稱用來標識這個型別,其次在我們進行獲取這個型別的時候需要在引數或者屬性用我們定義的第二個特性標記這個引數或者屬性是從容器中獲取的是哪一個名稱哪一個型別的物件,這樣就可以實現一個屬性注入和一個介面多實現的一個操作。
總結
以上是我個人實現自定義IOC的一個解決思路,並且在net core5以及net core6中實現,且5到6實現了無縫升級,沒有任何錯誤,希望能夠對各位讀者有所幫助。還是希望眾多道友能夠多解讀原始碼,去檢視core框架開發者提供給我們暴露給我們的自定義擴充的一些介面和抽象類。後面我依舊會持續更新core自定義相關的東西,會包括配置還有日誌等其他方面的東西,多執行緒方面的程式碼已經寫完,可以在QQ群6406277群檔案中進行查詢,也可以檢視哪個net群有叫四川觀察的,那個就是我。IL後面我也寫了很多東西,後續也會一一奉上。在此,謝謝各位看官瀏覽。