Core官方DI解析(5)-ServiceProviderEngine

来源:https://www.cnblogs.com/yan7/archive/2018/11/30/10042170.html
-Advertisement-
Play Games

最後來看看前面一直說的 Engine(工作引擎) ,工作引擎介面是 在`ServiceProvider IServiceProviderEngine`介面和其實現類的整體結構 IServiceProviderEngine類型繼承關係 繼承了 介面,也就是說工作引擎也具有 GetService() 方 ...


最後來看看前面一直說的Engine(工作引擎),工作引擎介面是IServiceProviderEngineServiceProvider的構造函數中看到了根據指定的Mode創建了不同的實現類,下麵先來看一下IServiceProviderEngine介面和其實現類的整體結構

IServiceProviderEngine類型繼承關係

internal interface IServiceProviderEngine : IDisposable, IServiceProvider
 {
      IServiceScope RootScope { get; }
 }
`IServiceProvderEngine這個介面`繼承了`IServiceProvider`介面,也就是說工作引擎也具有**GetService()**方法,在此介面中具有一個`IServiceScope`類型的**RootScope**,而這個屬性則代表是**根容器**

`IServiceScope`代表一個容器介面,這個介面中具有一個`IServiceProvider`類型的屬性,返回真正表示容器的一個`IServiceProvider`類型            
public interface IServiceScope : IDisposable
{
   /// <summary>
   /// The <see cref="System.IServiceProvider"/> used to resolve dependencies from the scope.
   /// </summary>
   IServiceProvider ServiceProvider { get; }
}   

IServiceProviderEngine整體結構

IServiceProviderEngine

  • ServiceProviderEngine
    • CompiledServiceProviderEngine
      • DynamicServiceProviderEngine
    • RuntimeServiceProviderEngine
    • ILEmitServiceProviderEngine
    • ExpressionsServiceProviderEngine
上面是目前整個引擎結構,但是前面說過目前只用到了`DynamicServiceProviderEngine`,但是我們看整個類型會看到其實在這幾個派生類型中只有一個實現方法`RealizeService(ServiceCallSite callSite)`,而整體結構都是在基類`ServiceProviderEngine`類型中,下麵來看看這個基類類型

ServiceProviderEngine

`ServiceProviderEngine`類型是整個結構的核心類型,但是這個類也是一個很簡單的類,這個類只是調用`CallSiteFactory`和`CallSiteRuntimeResolver`,由下圖可以看到這個類型是一個抽象類,並且實現了`IServiceProviderEngine`和`IServiceScopeFactory`介面介面            

`IServiceScopeFactory`這個介面提供了一個創建子容器方法我們已知道`IServiceProviderEngine`介面*繼承*了`IServiceProvider`介面,那麼也就是說在`ServiceProviderEngine`已經具備以下兩個功能 

1.獲取服務實例對象

2.創建子容器

internal abstract class ServiceProviderEngine : IServiceProviderEngine, IServiceScopeFactory{}


//      創建子容器介面
public interface IServiceScopeFactory
{
     //     
     IServiceScope CreateScope();
}

下麵首先來看一下此類中擁有的欄位+屬性,這些屬性都是在構造器中進行了實例化

  • _callback:

    這個欄位就是頂級容器時檢查scoped生命周期的訪問者對象,這個從ServiceProvider類中時進行傳入的,在這裡並不細講這個類型

  • RealizedServices:

    這個屬性是緩存根據容器獲取服務實例對象委托,其中Key為ServiceType

  • _createServiceAccessor:

    這是一個根據類型獲取一個根據容器獲取服務實例對象的委托,可以看到使用了一個CreateServiceAccessor()進行賦值,CreateServiceAccessor()是此類型的一個核心方法,下麵介紹

  • CallSiteFactory:

    ServiceCallSite工廠類型,在構造器中實例化,可以看到實例化時將serviceDescriptors進行傳入,並且可以看到在構造器中向此實例對象中添加了一個IServiceProviderIServiceScopeFactory

  • RuntimeResolver:

    這個屬性是是獲取服務實例的訪問者對象,可以看到在構造器中進行傳入

  • Root:

    Root代表是一個頂級容器ServiceProviderEngineScope類型則是一個具體的容器類型,這個類型中緩存了所有的具體服務實例對象,這個類型實現了IServiceScope介面,從下麵代碼可以看到RootScope其實就是直接返回了Root屬性

  • RootScope:

    這也是一個根容器實例對象,直接返回的Root屬性

//      頂級容器時scoped生命周期實例檢查策略   
private readonly IServiceProviderEngineCallback _callback;
//      根據類型創建構建服務的委托
private readonly Func<Type, Func<ServiceProviderEngineScope, object>> _createServiceAccessor;
//      此實例是否被銷毀
private bool _disposed;

//      緩存根據容器獲取服務實例的委托,  Key為註冊類型
internal ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object>> RealizedServices { get; }

 //     CallSite工廠類屬性,此類型用於根據指定實例化方式來創建對應的CallSite
internal CallSiteFactory CallSiteFactory { get; }
//      訪問者對象,此對象對進行實例和緩存具體真正的對象
protected CallSiteRuntimeResolver RuntimeResolver { get; }

//      根容器實例屬性
public ServiceProviderEngineScope Root { get; }
//      根容器實例屬性
public IServiceScope RootScope => Root;

//      構造器
protected ServiceProviderEngine(IEnumerable<ServiceDescriptor> serviceDescriptors, IServiceProviderEngineCallback callback)
{
     _createServiceAccessor = CreateServiceAccessor;
     _callback = callback;
     //      實例化根容器
     Root = new ServiceProviderEngineScope(this);
     //      實例化 CallSite對象訪問者對象
     RuntimeResolver = new CallSiteRuntimeResolver();

     //      實例化CallSiteFactory類型對象
     CallSiteFactory = new CallSiteFactory(serviceDescriptors);
     CallSiteFactory.Add(typeof(IServiceProvider), new ServiceProviderCallSite());
     //      緩存一個ServiceScopeFactoryCallSite服務,相當於緩存一個ServiceProviderEngine,根據此對象進行創建子容器
     CallSiteFactory.Add(typeof(IServiceScopeFactory), new ServiceScopeFactoryCallSite());
     //     緩存實例化對象的工廠
     RealizedServices = new ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object>>();
}
`ServiceProviderEngine`類型中方法只有**GetService()**,**CreateScope()**,**CreateServiceAccessor()**,**Dispose()**和一個抽象方法**RealizeService()**,其中幾個派生類中都只是實現了**RealizeService()**,這個一會再看,下麵來看看`ServiceProviderEngine`類中的這幾個方法
  • RealizeService:

    這個方法由派生類繼承,由指定的ServiceCallSite緩存並獲取 服務實例的委托

  • GetService:

    這個方法獲取服務實例對象,可以看到具有兩個此方法,並且第一個調用了第二個,並將頂級容器Root進行了傳入,而在第二個方法中,獲取並添加**_createServiceAccessor**委托,然後調用此委托進行獲取服務實例

  • CreateScope:

    這個方法是創建一個子容器對象,在這個方法中可以看到直接 new 了一個容器對象,並將當前對象進行了傳入。從此可以得知為什麼所有容器共用頂級容器的服務註冊了

  • Dispose:

    清除當前對象,並清除頂級容器

  • CreateServiceAccessor:

    這個方法可以看到根據ServiceType進行獲取指定ServiceCallSite,然後再調用派生類實現的RealizeService()進行返回

//      抽象類型,子類實現
protected abstract Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite);

public object GetService(Type serviceType) => GetService(serviceType, Root);

internal object GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
{
     if (_disposed)
        ThrowHelper.ThrowObjectDisposedException();
     //      添加並獲取根據容器對象實例化對象的方法,其方法由子類進行重寫
     var realizedService = RealizedServices.GetOrAdd(serviceType, _createServiceAccessor);
     //      驗證是否允許進行實例化對象
     _callback?.OnResolve(serviceType, serviceProviderEngineScope);
     return realizedService.Invoke(serviceProviderEngineScope);
}
public void Dispose()
{
     _disposed = true;
     Root.Dispose();
}
//      實例化的子容器
public IServiceScope CreateScope()
{
     if (_disposed)
          ThrowHelper.ThrowObjectDisposedException();
     return new ServiceProviderEngineScope(this);
}
private Func<ServiceProviderEngineScope, object> CreateServiceAccessor(Type serviceType)
{
     //      根據基類類型獲取對應的CallSite
     var callSite = CallSiteFactory.GetCallSite(serviceType, new CallSiteChain());
     if (callSite != null)
     {
          //  緩存當前註冊
          _callback?.OnCreate(callSite);
          return RealizeService(callSite);
     }
     return _ => null;
}

DynamicServiceProviderEngine和CompiledServiceProviderEngine

下麵來看一下`DynamicServiceProviderEngine`和`CompiledServiceProviderEngine`這兩個派生類型,從上面繼承關係可以看到這兩個派生類型是一個繼承關係 `DynamicServiceProviderEngine`繼承於`CompiledServiceProviderEngine`.
internal class DynamicServiceProviderEngine : CompiledServiceProviderEngine{}

​在這兩個派生類型中都只是實現了基類的RealizeService(),下麵先來看看CompiledServiceProviderEngine類的實現

可以看到CompiledServiceProviderEngine類中具有一個ExpressionResolverBuilder對象,這個類是使用表達式樹生成結構,這個實例在構造函數進行創建,並且將CallSiteRuntimeResolver對象,本對象和頂級容器進行了傳入,可以看到在重寫的方法中是調用了ExpressionResolverBuilder對象的Build(),這個方法會生成一個Func<ServiceProviderEngineScope,Object>委托,然後緩存此委托,

註:ExpressionResolverBuilder這個類挺複雜,我也沒看懂,所以在此不介紹,有興趣的可以直接看源碼

internal abstract class CompiledServiceProviderEngine : ServiceProviderEngine
{
    //      表達式樹生成對象
     public ExpressionResolverBuilder ExpressionResolverBuilder { get; }
     
     //     構造函數
     public CompiledServiceProviderEngine(IEnumerable<ServiceDescriptor> serviceDescriptors, IServiceProviderEngineCallback callback) : base(serviceDescriptors, callback)
        => ExpressionResolverBuilder = new ExpressionResolverBuilder(RuntimeResolver, this, Root);
     
     //     重寫RealizeService方法
     protected override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
     {
          //      使用表達式樹進行創建一個Func<ServiceProviderEngineScope,Object>委托
          var realizedService = ExpressionResolverBuilder.Build(callSite);
          //      直接將表達式生成的委托進行替換之前的緩存
          RealizedServices[callSite.ServiceType] = realizedService;
          return realizedService;
     }
}
而在`RuntimeServiceProviderEngine`類中,則只是實現了**RealizeService()**,從下麵代碼可以看出在第一次調用時是直接調用`CallSiteRuntimeResolver`這個訪問者獲取的實例數據,而在第二次才調用的基類,也就是`CompiledServiceProviderEngine`進行了緩存,但是至於為什麼這樣乾,我沒有弄清。。。
internal class DynamicServiceProviderEngine : CompiledServiceProviderEngine
{
     public DynamicServiceProviderEngine(
     IEnumerable<ServiceDescriptor> serviceDescriptors, 
     IServiceProviderEngineCallback callback) 
     : base(serviceDescriptors, callback)
     {
     }

     protected override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
     {
          var callCount = 0;
          return scope =>
          {
               if (Interlocked.Increment(ref callCount) == 2)
               {
                    //      如果當前是第二次調用,則調用父級進行緩存
                    Task.Run(() => base.RealizeService(callSite));
               }
               //      調用訪問者進行根據當前容器和CallSite進行實例化服務對象
               return RuntimeResolver.Resolve(callSite, scope);
          };
     }
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 摘要:本文主要對Java這門編程語言進行簡單的介紹。 Java簡介 說明 Java語言歷時十多年,已發展成為人類電腦史上影響深遠的編程語言,從某種程度上來看,它甚至超出了編程語言的範疇,成為一種開發平臺,一種開發規範。Java語言所崇尚的開源、自由等精神,吸引了全世界無數優秀的程式員。事實是,從人 ...
  • 1.管道 進程間通信(IPC)方式二:管道(不推薦使用,瞭解即可),埠易導致數據不安全的情況出現。 2.共用數據 進程之間數據共用的模塊之一Manager模塊(少用): 進程間數據是獨立的,可以藉助於隊列或管道實現通信,二者都是基於消息傳遞的雖然進程間數據獨立,但可以通過Manager實現數據共用 ...
  • 一、input()函數 在 Python 中,使用內置函數 input()可以接收用戶的鍵盤輸入。 input()函數的基本用法如 下: 其中,variable 為保存輸入結果的變數,雙引號內的文字用於提示要輸入的內容。 二、print()函數預設的情況下,在Python中,使用內置的print() ...
  • 一:EL表達式 1.概述:在jsp開發中,為了獲取Servlet域對象中存儲的數據,經常要寫很多java代碼,這樣的做法會使JSP頁面混亂,難以維護,為此,在JSP2.0規範中提供了EL表達式。它是Expression Language的縮寫。 2.語法:${表達式} 2.1內置對象: 2.1.1獲 ...
  • 一、引言 官網文檔:http://www.mybatis.org/generator/index.html 通過使用官方提供的mapper自動生成工具,mybatis-generator-core-1.3.2來自動生成po類和mapper映射文件。 作用:mybatis官方提供逆向工程,可以使用它通 ...
  • 1. 什麼是列表 定義: 能裝對象的對象 在python中使用 [] 來描述列表, 內部元素用逗號隔開. 對數據類型沒有要求 列表存在索引和切片. 和字元串是一樣的. 2. 相關的增刪改查操作 添加: 1. append() 追加 2. insert(位置, 元素) 插入指定元素到指定位置 刪除: ...
  • 題意 "題目鏈接" Sol 神仙題Orzzzz 題目可以轉化為從$\leqslant M$的質數中選出$N$個$xor$和為$0$的方案數 這樣就好做多了 設$f(x) = [x \text{是質數}]$ $n$次異或FWT即可 快速冪優化一下,中間不用IFWT,最後轉一次就行(~~然而並不知道為什 ...
  • 1.進程同步/串列(鎖) 進程之間數據不共用,但共用同一套文件系統,所以訪問同一個文件,或同一個列印終端,沒有問題,但共用帶來的是競爭容易錯亂,如搶票時。這就需讓進程一個個的進去保證數據安全,也就是加鎖處理,Lock 併發,效率高,但是競爭同一個文件時,導致數據混亂 加鎖,由併發改成了串列,犧牲了運 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...