.NET 中依賴注入元件 Autofac 的效能漫聊

Soar、毅發表於2021-01-29

Autofac 是一款超讚的 .NET IoC 容器 ,在眾多效能測評中,它也是表現最優秀的一個。它管理類之間的依賴關係, 從而使 應用在規模及複雜性增長的情況下依然可以輕易地修改。它的實現方式是將常規的.net類當做 元件 處理。

簡單的效能測試

在 LINQPad 中,我們可以很容易的構建出一個測試環境(需要引入 Microsoft.Extensions.DependencyInjection 和 Autofac.Extensions.DependencyInjection 元件):

圖片

寫一些簡單的效能進行測試程式碼:

圖片

在 LINQPad 中對上述程式碼進行一萬次、十萬次、百萬次三個量級的測試,得出以下報表(縱軸單位為“毫秒”):

圖片

從統計圖中可以看到,即便是最耗時的 Transient 物件,百萬級別建立的時間消耗也不到 400 毫秒。這說明,大多數情況下 Autofac 之類的 IoC 容器不會成為應用的效能瓶頸。

建構函式爆炸

當一個系統不斷完善,業務在底層會被不斷拆分為小的 Service ,然後在頂層(應用層或表現層)組裝以完成功能。這表示在 Controller 中我們需要注入大量的 Service 才能保證功能完備。如果我們需要的物件通過建構函式注入,那麼就會造成該建構函式的引數多到爆炸。

nopCommerce 是一個 ASP.NET 開發的電子商城系統,具備商城該有的各種功能和特性。在 ShoppingCartController 中你可以看到以下程式碼:

圖片

建構函式爆炸的效能問題

即便引數再多,在感官上也只是一個強迫症的問題。但建構函式爆炸所造成的影響不僅僅只是看上去沒那麼舒服而已。當我們注入一個物件時,IoC 容器會保證該物件以及其依賴的物件已經被正確初始化。所以我們不能簡單的根據注入物件的數量來判斷效能消耗,因為很有可能某個介面的實現依賴了數個其他物件。

當我們訪問一個網頁時,只會用到 Controller 中的某一個方法,通常,該方法不會對所有注入的物件都產生依賴。這也意味著我們建立了大量非必要的物件,為記憶體和 GC 造成了壓力。

在 ASP.NET Core 中解決建構函式爆炸問題

ASP.NET Core 提供了一個名為 FromServicesAttribute 的屬性來幫助解決必須在建構函式中注入依賴的問題。我們可以為 Action 的引數增加此屬性,將所需的依賴注入進來:

圖片

這當然解決了建構函式爆炸的問題,很好。但同時,該方案也讓方法的引數變得複雜也為單元測試留下了障礙,依舊不夠完美。

使用 IServiceProvider 解決建構函式爆炸問題

在依賴注入容器中包含的所有物件都可以通過 IServiceProvider 獲取到。基於該特性可以實現依賴物件的按需載入功能:

圖片

以上程式碼在 MyService 的建構函式中輸出了建立日誌。MyController 型別中通過 LazyGetRequiredService 方法實現了 MyService 的按需載入,建構函式也只剩下一個 IServiceProvider 物件。以上程式碼會產生下面的輸出:

圖片

可以看到,在呼叫不依賴 MyService 的方法 NoCallMethod 時,MyService 並沒有被建立。直到 CallMethod 被呼叫後用到了 MyService 時,它才被建立。

向 Volo.Abp 學習

Volo.Abp 在 4.2.0 版本中加入了一個新的介面: IAbpLazyServiceProvider 。

圖片

其實現同樣採用 IServiceProvider 建立物件,同時使用了字典來儲存對例項的引用。如果你和我一樣使用 Abp 開發程式碼,那麼 LazyServiceProvider 值得嘗試。

相關文章