1. 程式人生 > >ASP.NET Core中的依賴注入(1):控制反轉(IoC)

ASP.NET Core中的依賴注入(1):控制反轉(IoC)

ASP.NET Core在啟動以及後續針對每個請求的處理過程中的各個環節都需要相應的元件提供相應的服務,為了方便對這些元件進行定製,ASP.NET通過定義介面的方式對它們進行了“標準化”,我們將這些標準化的元件稱為服務,ASP.NET在內部專門維護了一個DI容器來提供所需的服務。要了解這個DI容器以及現實其中的服務提供機制,我們先得知道什麼是DI(Dependence Injection),而一旦我們提到DI,又不得不說IoC(Inverse of Control)。

目錄
一、流程控制的反轉
二、對流程的定製
三、 IoC模式
    模板方法(Template Method)
    工廠方法(Factory Method)
    抽象工廠(Abstract Factory)

一、流程控制的反轉

我聽到很多人將IoC說成是一種“面向物件的設計模式”,但在我個人看來IoC不能算作一種“設計模式”,其自身也與“面向物件”沒有直接的關係。很多人之所以不能很準確地理解IoC源於他們忽略了一個最根本的東西,那就是IoC這個短語。換句話說,很多人之所以對IoC產生了諸多誤解是因為他們忽略了IoC的定義。

IoC的全名Inverse of Control,翻譯成中文就是“控制反轉”或者“控制倒置”。控制反轉也好,控制倒置也罷,它體現的意思是控制權的轉移,即原來控制權在A手中,現在需要B來接管。那麼具體對於軟體設計來說,IoC所謂的控制權的轉移具有怎樣的體現呢?要回答這個問題,就需要先了解IoC的C(Control)究竟指的是怎樣一種控制,在我看來這裡所謂的控制更多地體現為一種“流程的控制

”。

我們通過一個具體事例來說明傳統的設計在採用了IoC之後針對流程的控制是如何實現反轉的。比如說我們現在設計一個針對Web的MVC類庫,不妨將其命名為MvcLib。MvcLib提供瞭如下所示的API幫助我們完成整個HTTP請求流程中的主要任務。具體來說,ListenAndReceiveRequest方法啟動一個監聽器繫結到指定的地址進行請求的監聽,接收到的請求通過一個Request物件返回。ActivateController方法根據接收到的請求解析並激活請求的目標Controller。ExecuteContrller方法執行啟用的Controller並返回一個表示檢視的View物件。RenderView最終將View物件轉換成HTML並作為當前請求響應的內容。

   1: public static class MvcLib
   2: {
   3:     public static Request ListenAndReceiveRequest(Uri address);
   4:     public static Controller ActivateController (Request request);
   5:     public static View ExecuteContrller(Controller controller);
   6:     public static void RenderView(View view);
   7: }
   8:  

現在我們在這個MvcLib的基礎上建立一個真正的MVC應用,那麼除了按照MvcLib的規範自定義具體的Controller和View之外,我們還需要自行控制從請求的監聽與接收、Controller的啟用與執行以及View的最終呈現在內的整個流程,這樣一個執行流程反映在如下所示的程式碼中。

   1: public class App
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         Uri address = new Uri("http://localhost/mvcapp");
   6:         while (true)
   7:             {
   8:                 Request request = MvcLib.ListenAndReceiveRequest(address);
   9:                 Task.Run(()=> ProcessRequest(request));
  10:             }
  11:         }
  12:  
  13:     private static void ProcessRequest(Request request)
  14:     {
  15:         Controller controller = MvcLib.ActiveController(request);
  16:         View view = MvcLib.ExecuteContrller(controller);
  17:         MvcLib.RenderView(view);
  18:     }
  19: }

這個例子體現了右如圖所示的流程控制方式,即我們設計的類庫(MvcLib)僅僅通過API的形式提供某種單一功能的實現,作為類庫消費者的應用程式(App)則需要自行編排整個工作流程。如果從重用的角度來講,這裡被重用的僅限於實現某個環節單一功能的程式碼,編排整個工作流程的程式碼並沒有得到重用。

3-1

當我們構建一個應用的時候,我們不僅僅是需要一個能夠提供API的類庫,實際上更理想的形式是直接在一個現有的框架上構架我們的應用。類庫(Library)和框架(Framework)的不同之處在於,前者往往只是提供實現某種單一功能的API,而後者則針對一個目標任務對這些單一功能進行編排形成一個完整的流程,這個流程在一個引擎的驅動下被執行

在我們上面演示MvcLib來說,使用它的應用程式需要自行控制整個HTTP請求的處理流程,實際上這是一個很“泛化”的工作流程,幾乎所有的MVC應用均採用這樣的流程監聽、接收請求並最終對請求予以響應。如果我們將這個流程實現在一個MVC框架之中,由它構建的所有MVC應用就可以直接使用這個請求處理流程,而並不需要自行重複實現它。

3-2現在我們將MvcLib從類庫改造成一個框架,並姑且將其稱為MvcFrame。如左圖所示,MvcFrame的核心是一個被稱為MvcEngine的執行引擎,它驅動一個編排好的工作流對HTTP請求進行一致性處理。如果我們利用MvcFrame構建一個具體的MVC應用,除了根據我們的業務需求定義相應的Controller和View之外,我們只需要初始化這個引擎並直接啟動它即可。如果你曾經開發過ASP.NET MVC應用,你會發現ASP.NET MVC就是這麼一個框架。3-3

有了上面演示的這個例子作為鋪墊,我們應該很容易理解IoC所謂的控制反轉了。總的來說,IoC是我們設計框架所採用的設計思想,所謂的控制反轉即是按照如右圖所示的方式將原來實現在應用程式流程控制轉移到框架中。被轉移的是一個泛化的可重用的處理流程,所以IoC符合軟體設計一個基本的原則,即重用性。

二、對流程的定製

3-4我們採用IoC實現了流程控制從應用程式向框架自身的反轉,但是這個被反轉的僅僅是一個泛化的流程,任何一個具體的應用都可能需要對組成該流程的某些環節進行定製。如左圖所示,我們將一個泛化的工作流程(A=>B=>C)被定義在框架之中,建立在該框架的兩個應用需要對組成這個流程的某些環節進行定製。比如步驟A和C可以被App1重用,但是步驟B卻需要被定製(B1),App2則重用步驟A和B,但是需要按照自己的方式(C2)處理步驟C。

IoC將對流程的控制從應用程式轉移到框架之中,框架利用一個引擎驅動整個流程的執行。換句話說,應用程式無需關心該工作流程的細節,它只需要啟動這個引擎即可。但是這個引擎一旦被啟動,框架就會完全按照預先編排好的流程進行工作,如果應用程式希望整個流程按照自己希望的方式被執行,針對流程的定製一般在發生在啟動引擎之前。

一般來說,框架會以相應的形式提供一系列的擴充套件點,應用程式則通過定義擴充套件的方式實現對流程某個環節的定製。在引擎被啟動之前,應用程式將所需的擴充套件註冊到框架之中。一旦引擎被正常啟動,這些註冊的擴充套件會自動參與到整個流程的執行過程中。

雖然應用程式是框架引擎的啟動著,但是一旦引擎被啟動之後它就喪失了對流程的控制,應用程式對流程的定製不是在執行過程中對框架的干預來完成的,而只需要在流程執行之前就將定製的部分準備好,框架自身在執行過程中會智慧地選擇它們。從這個意義上講,IoC對流程的定製遵循著這樣一個原則,即“Don't call us, we'll call you!”,它被稱為好萊塢原則

綜上所述,IoC一方面通過流程控制從應用程式向框架的反轉實現了針對流程自身的重用,另一方面採用“好萊塢原則”使得這個被重用的流程可能自由地被定製,這兩個因素實際上決定了框架自身的價值。重用讓框架不僅僅是為應用程式提供實現單一功能的API,而是提供一整套可執行的解決方案;可定製則使我們可以為不同的應用程式對框架進行定製,這無疑讓框架可以使用到更多的應用之中。

三、 IoC模式

正如我們在上面提到過的,很多人將IoC理解為一種“面向物件的設計模式”,實際上IoC自身不僅與面向物件沒有必然的聯絡,它也算不上是一種設計模式。一般來講,設計模式提供了一種解決某種具體問題的方案,但是IoC既沒有一個針對性的問題領域,其自身沒有提供一種可實施的解決方案,所以我更加傾向於將IoC視為一種設計原則,實際上很多我們熟悉的設計模式背後採用了IoC原則。

模板方法(Template Method)

提到IoC,很多人首先想到的是DI,但是在我看來與IoC聯絡得最為緊密的則是另一種被稱為“模板方法”的設計模式。模板方法模式與IoC的意圖可以說完全一致,該模式主張將一個可複用的工作流程或者由多個步驟組成的演算法定義成模板方法,組成這個流程或者演算法的步驟則實現在相應的虛方法之中,模板方法根據流程先後呼叫這些虛方法。所有這些方法均定義在同一個類中,我們可以通過派生該類並重寫相應的虛方法達到對流程定製的目的。

對於上面我們演示的這個MVC的例子,我們可以將整個請求處理流程實現在如下一個MvcEngine類中,請求的監聽與接收、目標Controller的啟用與執行以及View的呈現則分別定義在四個受保護的虛方法中,模板方法Start根據預定義的請求處理流程先後呼叫這四個方法。

   1: public class MvcEngine
   2: {
   3:     public void Start(Uri address)
   4:     {
   5:         while (true)
   6:         {
   7:             Request request = this.OnListenAndReceiveRequest(address);
   8:             Task.Run(() =>
   9:             {
  10:                 Controller controller = this.OnActivateController(request);
  11:                 View       view       = this.OnExecuteContrller(controller);
  12:                 this.OnRenderView(view);
  13:             });
  14:         }
  15:     }
  16:     protected virtual Request OnListenAndReceiveRequest(Uri address) ;
  17:     protected virtual Controller OnActivateController(Request request) ;
  18:     protected virtual View OnExecuteContrller(Controller controller) ;        
  19:     protected virtual void OnRenderView(View view) ;
  20: }

對於具體的應用來說,如果定義在MvcEngine針對請求的處理方式完全符合它的要求,它只需要建立這個一個MvcEngine物件,然後指定一個對應的基地址呼叫模板方法Start開啟這個MVC引擎即可。如果該MVC引擎處理請求的某個環節不能滿足它的要求,它可以建立MvcEngine的派生類,並重寫實現該環節的相應虛方法即可。比如說定義在某個應用程式中的Controller都是無狀態的,它希望採用單例(Singleton)的方式重用已經啟用的Controller以提高效能,那麼它就可以按照如下的方式建立一個自定義的FoobarMvcEngine並按照自己的方式重寫OnActivateController方法既可。

   1: public class FoobarMvcEngine : MvcEngine
   2: {
   3:     protected override View OnExecuteContrller(Controller controller)
   4:     {
   5:         <<省略實現>>
   6:     }
   7: }

模板方法如果結合“事件註冊”往往可以使應用程式對流程的定製變得更加自由。如下面的程式碼片段所示,我們為Controller的啟用與執行以及View的呈現定義了六個事件,它們分別在這個三個環節開始之前和結束之後被觸發。這麼一個MvcEngine可以直接被使用,應用程式只需要註冊相應的事件完成對請求處理流程的定製。

   1: public class MvcEngine
   2: {
   3:     //其他成員
   4:     protected virtual Controller OnActivateController(Request request) ;
   5:     protected virtual View OnExecuteContrller(Controller controller) ;        
   6:     protected virtual void OnRenderView(View view) ;
   7:  
   8:     public EventHandler<ControllerActivationEventArgs> ControllerActivating;
   9:     public EventHandler<ControllerActivationEventArgs> ControllerActivated;
  10:     public EventHandler<ControllerExecutionEventArgs> ControllerExecuting;
  11:     public EventHandler<ControllerExecutionEventArgs> ControllerExecuted;
  12:     public EventHandler<ViewRenderEventArgs> ViewRendering;
  13:     public EventHandler<ViewRenderEventArgs> ViewRendered;
  14: }

工廠方法(Factory Method)

對於一個複雜的流程來說,我們傾向於將組成該流程的各個環節實現在相應獨立的元件之中,那麼針對流程的定製就可以通過提供不同元件的形式來實現。我們知道23種設計模式之中有一種重要的型別,那就是“建立型模式”,比如常用的“工廠方法”和“抽象工廠”,那麼IoC所體現的針對流程的共享與定製可以通過它們來完成。

3-5所謂的工廠方法,說白了就是在某個類中用於提供依賴物件的方法,這個方法可以是一個單純的虛方法,也可以是具有預設實現的虛方法,至於方法宣告的返回型別,可以是一個介面或者抽象類,也可以是未被封閉(Sealed)的具體型別。作為它的派生型別,它可以實現或者重寫工廠方法以提供所需的具體物件。

同樣以我們的MVC框架為例,我們讓獨立的物件來完成組成整個請求處理流程的四個核心環節。具體來說,我們分別定義了四個核心的型別(Listener、ControllerActivator、ControllerExecutor和ViewGenderer)來分別負責請求監聽與接收、Controller的啟用、Controller的執行以及View的呈現。這四個物件按照如右圖所示的順序相互協作完成對請求的處理。

如下所示的Listener、ControllerActivator、ControllerExecutor和ViewGenderer這四個型別的簡單定義。我們沒有將它們定義成單純的抽象類或者介面,而是未被封閉可以被繼承的一般型別,定義其中的虛方法具有預設的實現。只有這些預設的實現方式無法滿足應用程式具體需求的時候,我們才需要定義相應的派生類。

   1: public class Listener
   2: {
   3:     public virtual Request Listen(Uri address) ;
   4: }
   5:  
   6: public class ControllerActivator
   7: {
   8:     public virtual Controller ActivateController(Request request) ;
   9: }
  10:  
  11: public class ControllerExecutor
  12: {
  13:     public virtual View ExecuteController(Controller controller) ;
  14: }
  15:  
  16: public class ViewRenderer
  17: {
  18:     public virtual void RenderView(View view) ;
  19: }

在作為MVC引擎的MvcEngine類中,我們定義了四個工廠方法(GetListener、GetControllerActivator、GetControllerExecutor和GetViewRenderer)來提供上述這四種類型的物件。這四個工廠方法均為具有預設實現的虛方法,它們預設提供上述四種類型的物件。在用於啟動引擎的Start方法中,我們利用這些工廠方法提供的物件來具體完成請求處理流程的各個核心環節。

   1: public class MvcEngine
   2: {
            
           

相關推薦

ASP.NET Core依賴注入1控制反轉IoC

ASP.NET Core在啟動以及後續針對每個請求的處理過程中的各個環節都需要相應的元件提供相應的服務,為了方便對這些元件進行定製,ASP.NET通過定義介面的方式對它們進行了“標準化”,我們將這些標準化的元件稱為服務,ASP.NET在內部專門維護了一個DI容器來提供所需的服務。要了解這個DI容器以及現實其中

.Net Core依賴注入服務使用總結

一、依賴注入   引入依賴注入的目的是為了解耦和。說白了就是面向介面程式設計,通過呼叫介面的方法,而不直接例項化物件去呼叫。這樣做的好處就是如果添加了另一個種實現類,不需要修改之前程式碼,只需要修改注入的地方將實現類替換。上面的說的通過介面呼叫方法,實際上還是需要去例項化介面的實現類,只不過不需要我們手動n

Spring學習1控制反轉IoC依賴注入DI的詳解以及註解annotation開發入門案例

前言 以往的java學習中,我們要想得到一個物件,就把它new出來。如:Apple apple = new Apple(); 在一些複雜的系統中,一個物件A可能依賴於物件B,C等(程式碼表現為A類持有B,C類的物件作為A類的屬性)。以往來說,我們想要使用B,

ASP.NET Core依賴注入3: 服務的註冊與提供

在採用了依賴注入的應用中,我們總是直接利用DI容器直接獲取所需的服務例項,換句話說,DI容器起到了一個服務提供者的角色,它能夠根據我們提供的服務描述資訊提供一個可用的服務物件。ASP.NET Core中的DI容器體現為一個實現了IServiceProvider介面的物件。 ServiceProvider與

ASP.NET Core依賴注入2依賴注入DI

IoC主要體現了這樣一種設計思想:通過將一組通用流程的控制從應用轉移到框架之中以實現對流程的複用,同時採用“好萊塢原則”是應用程式以被動的方式實現對流程的定製。我們可以採用若干設計模式以不同的方式實現IoC,比如我們在上面介紹的模板方法、工廠方法和抽象工廠,接下來我們介紹一種更為有價值的IoC模式,即依賴注入

ASP.NET Core依賴注入4: 建構函式的選擇與服務生命週期管理

ServiceProvider最終提供的服務例項都是根據對應的ServiceDescriptor建立的,對於一個具體的ServiceDescriptor物件來說,如果它的ImplementationInstance和ImplementationFactory屬性均為Null,那麼ServiceProvider

ASP.NET Core依賴注入5ServicePrvider實現揭祕【補充漏掉的細節】

到目前為止,我們定義的ServiceProvider已經實現了基本的服務提供和回收功能,但是依然漏掉了一些必需的細節特性。這些特性包括如何針對IServiceProvider介面提供一個ServiceProvider物件,何建立ServiceScope,以及如何提供一個服務例項的集合。 一、提供一個Serv

ASP.NET Core依賴注入5: ServiceProvider實現揭祕 【總體設計 】

本系列前面的文章我們主要以程式設計的角度對ASP.NET Core的依賴注入系統進行了詳細的介紹,如果讀者朋友們對這些內容具有深刻的理解,我相信你們已經可以正確是使用這些與依賴注入相關的API了。如果你還對這個依賴注入系統底層的實現原理具有好奇心,可以繼續閱讀這一節的內容。 目錄一、ServiceCall

ASP.NET Core依賴注入5: ServiceProvider實現揭祕 【解讀ServiceCallSite 】

通過上一篇的介紹我們應該對實現在ServiceProvider的總體設計有了一個大致的瞭解,但是我們刻意迴避一個重要的話題,即服務例項最終究竟是採用何種方式提供出來的。ServiceProvider最終採用何種方式提供我們所需的服務例項取決於最終選擇了怎樣的ServiceCallSite,而服務註冊是採用的S

ASP.NET Core使用GraphQL - 第三章 依賴注入

ASP.NET Core中使用GraphQL ASP.NET Core中使用GraphQL - 第一章 Hello World ASP.NET Core中使用GraphQL - 第二章 中介軟體 SOLID原則中的D表示依賴倒置原則。這個原則的內容是: 上層模組不應

ASP.NET Core如影隨形的”依賴注入”[上]: 從兩個不同的ServiceProvider說起

我們一致在說 ASP.NET Core廣泛地使用到了依賴注入,通過前面兩個系列的介紹,相信讀者朋友已經體會到了這一點。由於前面兩章已經涵蓋了依賴注入在管道構建過程中以及管道在處理請求過程的應用,但是內容相對分散和零碎,我們有必要針對這個主題作一個歸納性的介紹。採用依賴注入的服務均由某個ServiceProvi

ASP.NET Core如影隨形的”依賴注入”[下]: 歷數依賴注入的N種玩法

在對ASP.NET Core管道中關於依賴注入的兩個核心物件(ServiceCollection和ServiceProvider)有了足夠的認識之後,我們將關注的目光轉移到程式設計層面。在ASP.NET Core應用中基於依賴注入的程式設計主要涉及到兩個方面,它們分別是將服務註冊到ServiceCollect

Asp.net core依賴注入

原文: Asp.net core中的依賴注入 使用服務 在Asp.net core的Controller中,可以通過如下兩種方式獲取系統注入的服務: 建構函式 可以直接在建構函式中傳入所依賴的服務,這是非常常見的DI注入方式。     public 

WSL2+Docker部署RabbitMQ以及在Asp.net core 使用RabbitMQ示例1

        本文主要在於最近因疫情不能外出,在家研究的一些技術積累。         主要用到的技術以及知識點:  WSL 2 WSL 2+Docker Docker+RabbitMQ 在ASP.NET Core中

體驗 ASP.NET Core 的多語言支持Localization

lan expander -c blank 根據 body esp doc input 首先在 Startup 的 ConfigureServices 中添加 AddLocalization 與 AddViewLocalization 以及配置 RequestLocaliz

ASP.NET Core 如何給中間件傳參數轉載

inject its mes str project dsc format blank sam Passing Parameters to Middleware in ASP.NET Core 2.0 Problem How do you pass paramet

ASP.NET Core 的 WebSocket 支持轉自MSDN

ocs 接收 緩沖 任務 ica uget 本地服務器 tcp msdn 本文介紹 ASP.NET Core 中 WebSocket 的入門方法。 WebSocket (RFC 6455) 是一個協議,支持通過 TCP 連接建立持久的雙向信道。 它用於從快速實時通信中獲益的

Core使用Hangfire 在Asp.Net Core使用DI的方式使用Hangfire構建後臺執行指令碼 解決 ASP.NET Core Hangfire 未授權401 Unauthorized

    之前使用Quartz.Net,後來發現hangfire對Core的繼承更加的好,而且自帶管理後臺,這就比前者好用太多了。 安裝註冊 安裝 PM> Install-Package Hangfire Startup.cs,在ConfigureServices方法中添加註冊:

ASP.NET Core使用IOC三部曲(二.採用Autofac來替換IOC容器,並實現屬性注入)

https://www.cnblogs.com/GuZhenYin/p/8301500.html     上一篇我們說過ASP.NET Core中自帶的IOC容器是屬於輕量級的,功能並不是很多,只是提供了基礎功能而已.. 所以今天我們主要講講如何採用Autofac

ASP.NET Core 的 WebSocket 支援轉自MSDN

本文介紹 ASP.NET Core 中 WebSocket 的入門方法。 WebSocket (RFC 6455) 是一個協議,支援通過 TCP 連線建立持久的雙向通道。 它用於從快速實時通訊中獲益的應用,如聊天、儀表板和遊戲應用。 如果不明白什麼是WebSocket可以參考這篇文章   系統