- IOC: Inversion Of Control 控制反轉
- DI: Dependency Injection 依賴注入

1.控制反轉 Inversion Of Control 的前世今生
1.1 IOC理論產生的背景
討論控制反轉之前,先看看軟體系統提出控制反轉的前世今生,
一個完整精密的軟體系統,組件之間就像齒輪,協同作業,相互耦合,
- 一個零件不正常,整個系統就崩潰了,
- 系統物件之間耦合關系無法避免,在專案規模和復雜度變大的情況下,管理類之間的依賴關系將會很復雜,
- 物件之間耦合度很高的系統,架構師和開發人員對于系統的修改,必然會出現牽一發而動全身的情形,
- 物件之間耦合性依賴,單元測驗很復雜,
1.2 IOC理論
軟體專家為此提出IOC理論,用來實作物件之間的解耦,
再來看看,控制反轉(IOC)到底為什么要起這么個名字?我們來對比一下:
- 軟體系統在沒有引入IOC容器之前,物件A依賴于物件B,那么物件A在初始化或者運行到某一點的時候,自己必須主動去創建物件B或者使用已經創建的物件B,無論是創建還是使用物件B,控制權都在自己手上,
- 軟體系統在引入IOC容器之后,這種情形就完全改變了,由于IOC容器的加入,物件A與物件B之間失去了直接聯系,所以,當物件A運行到需要物件B的時候,IOC容器會主動創建一個物件B注入到物件A需要的地方,

通過前后對比,我們不難看出:
物件A獲得依賴物件B的程序,由主動變為了被動行為,控制權顛倒過來,這就是“控制反轉”的由來,
1.3 控制反轉 和 依賴注入
有些人會把控制反轉和依賴注入等同,實際上有本質區別:
控制反轉是一種思想;依賴注入是一種設計模式,
依賴注入是實作控制反轉的一種方式,但是控制反轉還有其他實作方式,例如說ServiceLocator(服務定位器、依賴查找),所以不能將控制反轉和依賴注入等同,

2 依賴注入 Dependency Injection
依賴注入:容器全權負責組件的裝配,它會把符合依賴關系的物件通過屬性或者建構式傳遞給需要的物件,
符合依賴倒置原則,高層模塊不應該依賴低層模塊,兩者都應該依賴其抽象
2.1 ASP.NET Core依賴注入
使用方式大體類似:
- 定義依賴實作的介面或者抽象類
- 在服務容器中注冊組件依賴 :
IServiceProvider - 在建構式中注入服務, 框架會負責創建和銷毀實體
1 // 撰寫組件和服務 2 public interface IMyDependency 3 { 4 string WriteMessage(string message); 5 } 6 --- 7 public class MyDependency : IMyDependency 8 { 9 public string WriteMessage(string message) 10 { 11 return $"MyDependency.WriteMessage Message: {message}"; 12 } 13 } 14 // 注冊組件和依賴,下面注冊的`IMyDependency`在一個web請求中有效 15 public void ConfigureServices(IServiceCollection services) 16 { 17 services.AddScoped<IMyDependency, MyDependency>(); 18 services.AddRazorPages(); 19 } 20 --- 21 // 在建構式注入組件 22 public class HomeController: AbpController 23 { 24 private readonly IMyDependency _dep; 25 public HomeController(IMyDependency dep) 26 { 27 _dep = dep; 28 } 29 30 public IActionResult Index() 31 { 32 var content = _dep.WriteMessage($"The Reflection instance is {_dep.GetType().FullName} "); 33 return Content(content); 34 } 35 }
在請求某個服務時,框架會完整決議出這個物件的依賴樹和作用范圍,
上面的示例代碼形成 req--->HomeController--->IMyDependency依賴樹,
IMyDependency在每個web請求范圍內使用同一服務實體,
輸出:MyDependency.WriteMessage Message: The Reflection instance is TestDI.MyDependency
2.2 物件生命周期
根據現實需要,前人從使用場景中總結出三種服務生命周期,
ASP.NET Core提供了一個列舉ServiceLifetime:
| -- | --- | --- | --- |
|---|---|---|---|
| Singleton | 單例 | 服務容器首次請求會創建,后續都使用同一實體 | AddSingleton |
| Scoped | 特定范圍 | 在一個請求(連接)周期內使用一個示例 | AddScoped |
| Transient | 瞬時 | 服務容器每次請求,都會創建一個實體 | AddTransient |
對于Scoped Service的理解:

在webapp:scoped service 會在請求結束時被銷毀;
在EFCore:使用AddDbContext默認注冊的是特定范圍的DbContext,這意味在我們可以在一次sql連接內,使用同一個DbContext實體進行多次DB操作,
2.3 依賴注入實作原理
結合理論、使用方式 猜測依賴注入的原理:
實作DI,核心在于依賴注入容器IContainer,該容器具有以下功能
①.(容器)保存可用服務的集合
// 要用的特定物件、特定類、介面服務
②.(注冊)提供一種方式將各種部件與他們依賴的服務系結到一起;
// Add...函式或containerBuilder.Register函式
③.(決議點)為應用程式提供一種方式來請求已配置的物件:建構式注入、屬性注入.
運行時,框架會一層層通過反射構造實體,最終得到完整物件,
3.原始碼導航
利用反射產生物件是依賴注入的核心程序,這也是面試造航母時經常問到的,
.NETSystem.Reflection、System.Type命名空間中的類可以獲取可裝配組件、類、介面的資訊,并提供了在運行時創建實體,呼叫動態實體方法、獲取動態實體的能力,
實際上,我們可以在依賴樹的尾部物件的建構式手動拋出例外,例外的呼叫堆疊就是一個天然的原始碼導航,
于是我在上面示例代碼的request----> HomeController--->MyDependency MyDependency建構式中添加例外代碼:
1 public MyDependency() 2 { 3 throw new Exception("exception content!"); 4 }
結果如下圖:

從Github Dependency Injection 庫進入System.Reflection的呼叫分界線代碼:
1 protected override object VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context) 2 { 3 object[] parameterValues; 4 if (constructorCallSite.ParameterCallSites.Length == 0) 5 { 6 parameterValues = Array.Empty<object>(); 7 } 8 else 9 { 10 parameterValues = new object[constructorCallSite.ParameterCallSites.Length]; 11 for (var index = 0; index < parameterValues.Length; index++) 12 { 13 parameterValues[index] = VisitCallSite(constructorCallSite.ParameterCallSites[index], context); 14 } 15 } 16 17 try 18 { 19 return constructorCallSite.ConstructorInfo.Invoke(parameterValues); 20 } 21 catch (Exception ex) when (ex.InnerException != null) 22 { 23 ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); 24 // The above line will always throw, but the compiler requires we throw explicitly. 25 throw; 26 } 27 }
黃色背景行就是.NET反射特性的體現:
對型別資訊(建構式、引數)使用Invoke方法產生物件,
干貨旁白
- 控制反轉是一種在軟體工程中解耦合的思想,呼叫方依賴介面或抽象類,減少了耦合,控制權交給了服務容器,由容器維護注冊項,并將具體的實作動態注入到呼叫方,
- 有些人會把控制反轉和依賴注入等同,實際上有本質區別:
- 控制反轉是一種思想;
- 依賴注入是一種設計模式,
- 依賴注入是實現控制反轉的一種方式,但是控制反轉還有其他實作方式,例如說
ServiceLocator,所以不能將控制反轉和依賴注入等同, - 在運行時,框架會決議依賴樹、依賴圖,通過反射在運行期生成物件,
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/184089.html
標籤:.NET Core
