天天看點

ASP.NET Core中的依賴注入(5): ServiceProvider實作揭秘 【總體設計 】一、ServiceCallSite二、Service三、ServiceEntry四、ServiceTable五、ServiceProvider

本系列前面的文章我們主要以程式設計的角度對ASP.NET

Core的依賴注入系統進行了詳細的介紹,如果讀者朋友們對這些内容具有深刻的了解,我相信你們已經可以正确是使用這些與依賴注入相關的API了。如果你還對這個依賴注入系統底層的實作原理具有好奇心,可以繼續閱讀這一節的内容。

目錄 一、ServiceCallSite 二、Service 三、ServiceEntry 四、ServiceTable 五、ServiceProvider

作為DI容器的展現,ServiceProvider是ASP.NET

Core依賴注入系統的一個核心對象,但是預設的實作者是一個定義在程式集

“Microsoft.Extensions.DependencyInjection.dll” 中的一個名為 “ServiceProvider”

内部(Internal)類型,而且它所依賴的很多接口和類型也是如此,是以我相信實作在這個ServiceProvider類中的服務提供機制對于絕大部分人是陌生的。本節提及的ServiceProvider不是泛指實作了IServiceProvider接口的類型,而是專指ServiceProvider這個内部類型。

為了讓讀者朋友們能夠深刻地了解ServiceProvider内部的實作原理,我會在本節内容中重新定義它。在這裡需要特别說明的是我們重建的ServiceProvider以及其他重建的接口和類旨在展現真實ServiceProvider設計思想和實作原理,在具體的源代碼層面是有差異的。考慮到篇幅的問題,很多細節的内容将不會展現在我們重建的接口和類型中。如果想了解原始的實作邏輯,可以從GitHub上下載下傳源代碼。

從總體設計的角度來審視ServiceProvider,需要涉及與之相關的4個核心對象,包括ServiceCallSite、Service、ServiceEntry和ServiceTable,它們均展現為相應的接口和類,并且這些接口和淚都是内部的,接下來我們就來逐一認識它們。

ServiceProvider的核心功能就是針對服務類型提供相應的服務執行個體,而服務執行個體的提供最終是通過ServiceCallSite來完成的。ServiceCallSite展現為具有如下定義的IServiceCallSite接口,除了直接提供服務執行個體的Invoke方法之外,它還具有另一個傳回類型為Expression的Build方法,該方法将定義在Invoke方法中的邏輯定義成一個表達式。

在真正提供服務執行個體的時候,ServiceProvider在收到針對某個服務類型的第一個服務擷取請求時,他會直接調用對應ServiceCallSite的Invoke方法傳回提供的服務執行個體。與此同時,這個ServiceCallSite的Build方法會被調用并生成一個表達式,該表達式進一步編譯成一個類型為Func<ServiceProvider,

object>的委托對象并被緩存起來。針對同一個服務類型的後續服務執行個體将直接使用這個緩存的委托對象來提供。

我們知道ServiceProvider提供服務的依據來源于建立它指定一個ServiceCollection對象,用于指導ServiceProvider如何提供所需服務的資訊以ServiceDescriptor對象的形式儲存在這個集合對象中。當ServiceProvider被初始化後,每一個ServiceDescriptor将會被轉換成一個Service對象,後者展現為如下一個IService接口。

Service的Lifetime屬性自然來源于ServiceDescriptor的同名屬性,它的CreateCallSite方法傳回一個針對用于提供對應服務執行個體的ServiceCallSite對象。由于Service對象可以建立ServiceCallSite,是以它自然具有提供服務執行個體的能力。Service總是作為連結清單的某個節點存在,這個連結清單是具有相同服務類型(對應ServiceType屬性)的多個ServiceDescriptot生成的,Service的Next屬性保持着對連結清單後一個節點的引用。

上面我們所說的由Service對象組成的連結清單展現為如下一個ServiceEntry類。我們為ServiceEntry定義了三個屬性(First、Last、All)分别代筆這個連結清單的第一個節點、最後一個節點以及所有節點,節點類型為IService。如果需要在鍊尾追加一個Service對象,可以直接調用Add方法。

多個ServiceEntry組成一個ServiceTable。如下面的代碼片段所示,一個ServiceTable通過其隻讀屬性ServieEntries維護着一組ServiceEntry對象與它們對應的服務類型之間的映射關系。一個ServiceTable對象通過一個ServiceCollection對象建立出來。如下面的代碼片段所示,組成ServiceCollection的所有ServiceDescriptor對象先根據其ServiceType屬性展現的服務類型進行分組,由每組ServiceDescriptor建立的ServiceEntry對象與對應的服務類型之間的映射會被添加到ServiceEntries屬性中。

從上面的代碼片段可以看出組成ServiceEntry的是一個類型為Service的對象,該類型定義如下。Service類實作了IService接口并通過一個ServiceDescriptor對象建立而成。我們省略了定義在方法CreateCallSite中建立ServiceCallSite的邏輯,後續在介紹各種類型的ServiceCallSite的時候我們會回來講述該方法的實作。

如下所示的代碼片段揭示了實作在ServiceProvider之中與服務提供和回收相關的基本實作原理。我們先來簡單介紹定義在它内部的幾個屬性。Root屬性傳回的ServiceProvider代表它的根,對于一個獨立的ServiceProvider來說,這個根就是它自己。ServiceTable屬性傳回根據ServiceCollection建立的ServiceTable對象。上面介紹ServiceCallSite的時候,我們提到它的Build方法傳回的表達式會編譯成一個類型為Func

<ServiceProvider,object>的委托,并被緩存起來服務于後續針對同一個類型的服務提供請求,該委托對象與對應服務類型之間的映射關系就儲存在RealizedServices屬性中。

對于采用Scoped模式提供的服務執行個體,ServiceProvider需要自行對它們進行維護,具體來說它們會和對應的Service對象之間的映射關系會儲存在ResolvedServices屬性中。如果采用Transient模式,對于提供過的服務執行個體,如果自身類型實作了IDisposble接口,它們會被添加到TransientDisposableServices屬性傳回的清單中。當Dispose方法執行的時候,這兩組對象的Dispose方法會被執行。

真正的服務提供機制展現在ServiceProvider實作的GetService方法中,實作邏輯其實很簡單:ServiceProvider會根據指定的服務類型從RealizedServices屬性中查找是否有通過編譯表達式生成的Func<ServiceProvider,object>委托生成出來,如果存在則直接使用它生成提供的服務執行個體。如果這樣的委托不存在,則會試着從ServiceTable中找到對應的ServiceEntry,如果不存在直接傳回Null,否則會調用ServiceEntry所在清單最後一個Service的CreateServiceCallSite方法建立一個ServiceCallSite對象(這一點說明了如果針對同一個服務類型注冊了多個ServiceDescriptor,在提供單個服務的時候總是使用最後一個ServiceDescriptor)。

接下來這個ServiceCallSite的Invoke方法被調用來建立服務執行個體,在傳回該執行個體之前它的Build方法會被調用,傳回的表達式被編譯成Func<ServiceProvider,object>委托并被添加到RealizedServices屬性中。如果ServiceProvider後續需要提供同類型的服務,這個委托對象将被啟用。

<a href="http://www.cnblogs.com/artech/p/asp-net-core-di-ioc.html">ASP.NET Core中的依賴注入(1):控制反轉(IoC)</a>

<a href="http://www.cnblogs.com/artech/p/asp-net-core-di-di.html">ASP.NET Core中的依賴注入(2):依賴注入(DI)</a>

<a href="http://www.cnblogs.com/artech/p/asp-net-core-di-register.html">ASP.NET Core中的依賴注入(3):服務注冊與提取</a>

<a href="http://www.cnblogs.com/artech/p/asp-net-core-di-life-time.html">ASP.NET Core中的依賴注入(4):構造函數的選擇與生命周期管理</a>

<a href="http://www.cnblogs.com/artech/p/asp-net-core-di-service-provider-1.html">ASP.NET Core中的依賴注入(5):ServicePrvider實作揭秘【總體設計】</a>

<a href="http://www.cnblogs.com/artech/p/asp-net-core-di-service-provider-2.html">ASP.NET Core中的依賴注入(5):ServicePrvider實作揭秘【解讀ServiceCallSite】</a>

<a href="http://www.cnblogs.com/artech/p/asp-net-core-di-service-provider-3.html">ASP.NET Core中的依賴注入(5):ServicePrvider實作揭秘【補充漏掉的細節】</a>

作者:蔣金楠

微信公衆賬号:大内老A

如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識别二維碼)關注個人公衆号(原來公衆帳号蔣金楠的自媒體将會停用)。

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。

<a href="http://www.cnblogs.com/artech/p/asp-net-core-di-service-provider-1.html" target="_blank">原文連結</a>

繼續閱讀