前言
這裡介紹一下.net core的依賴注入框架,其中其程式碼原理在我的另一個整理<<重新整理 1400篇>>中已經寫了,故而專門整理應用這一塊。
以下只是個人整理,如有問題,望請指點。
正文
為什麼有這個依賴注入呢?
假設人們面臨這樣一個問題。
比如說一個人做飛機去北京。那麼人和飛機有什麼關係呢?人和北京有什麼關係呢?
假設有3個主體類,一個是人一個是飛機一個是北京。他們之間到底應該怎麼組織呢?
先說人和北京的關係,人要去北京,那麼飛機就是一個交通工具。是的,只是一個交通工具,那麼人和飛機就沒有直接的關係,而是人通過交通工具去了北京。
同樣北京也可以抽象處理,北京是一個地點。那麼這個語句就變成了"人通過交通工具去了某個地方",這個交通工具可以是飛機可以是高鐵,未來可以是火箭。地點同樣如此。
如果抓住這個詞,“未來”,那麼其擴充套件性就很強了,不僅現在擁有的還代表以後。就像我們的usb介面一樣,未來可能有新的裝置只要適配了usb介面,那麼我主機裡面的東西將不用動任何東西就可以執行新的裝置。
因為我的主機沒有動任何東西,那麼是不是代表我的穩定性?所以依賴注入不僅可以確保我們程式碼的可維護性和可擴充套件性,最重要的是穩定性(當然了穩定性是可維護性的一種),而程式碼最重要的就是穩定性。
在asp.net core 的整個架構中,依賴注入框架提供了物件建立和生命週期管理的核心能力,各個元件相互協作,可以說是整個框架的核心了。
那麼這裡有一個關鍵詞生命週期,將會伴隨著整篇文章的核心。
.net core 的依賴實現主要是通過 Microsoft.Extensions.DependencyInjection;
其這個包,是實現了Microsoft.Extensions.DependencyInjection.Abstrations;
Microsoft.Extensions.DependencyInjection.Abstrations是介面的定義,Microsoft.Extensions.DependencyInjection 是介面的實現。
那麼這裡就可以想象到既然提供了介面,那麼一般來說就有第三方實現,後面會介紹第三方實現。
依賴注入的核心由下面幾個部門組成。
-
IServiceCollection 負責服務的註冊
-
ServiceDescriptor 每一個服務註冊時候的資訊
-
IServiceProvider 具體的容器
-
IServiceScope 容器的子容器的生命週期
那麼什麼是生命週期呢?
-
單例模式
-
作用域模式
-
瞬時模式
那麼這個怎麼理解呢?首先建立起這樣一個模型。
這個模型是什麼意思呢? 首先我們建立單例模式產生的物件會存在root中,如果作用域模式那麼建立的物件將會在某個作用域中,這個作用域是自己指定的。
而瞬時模式呢,就是不儲存在任何一個作用域中。
當我們通過這個依賴注入服務建立物件的時候,如果是單例模式,那麼會這麼幹,查詢root容器中有沒有該註冊資訊的例項,如果有就直接返回,如果沒有那麼建立儲存在root容器中。
如果是作用域模式,那麼會去找當前作用域有沒有該註冊資訊的例項,如果有那麼直接返回,如果沒有那麼儲存在當前作用域的容器中。
同樣,如果作用域模式,那麼該作用域的容器消失了,裡面儲存的東西就沒了,那麼自然通過該作用域建立的物件就消失了。
那麼就用程式碼來實驗一下。
有三個類:
裡面的內容就是一個介面,然後一個具體的類,這裡演示其中一個。
public interface IMySingletonService
{
}
public class MySingletonService: IMySingletonService
{
}
然後再configureServices 中進行註冊:
services.AddSingleton<IMySingletonService, MySingletonService>();
services.AddScoped<IMyScopedService, MyScopedService>();
services.AddTransient<IMyTransientService, MyTransientService>();
然後寫一個http的get方法。
[HttpGet]
public int GetService([FromServices] IMySingletonService mySingleton1,
[FromServices] IMySingletonService mySingleton2,
[FromServices]IMyScopedService myScoped1,
[FromServices] IMyScopedService myScoped2,
[FromServices] IMyTransientService myTransient1,
[FromServices] IMyTransientService myTransient2)
{
Console.WriteLine($"singleton1:{mySingleton1.GetHashCode()}");
Console.WriteLine($"singleton1:{mySingleton2.GetHashCode()}");
Console.WriteLine($"myScoped1:{myScoped1.GetHashCode()}");
Console.WriteLine($"myScoped2:{myScoped2.GetHashCode()}");
Console.WriteLine($"myTransient1:{myTransient1.GetHashCode()}");
Console.WriteLine($"myTransient2:{myTransient2.GetHashCode()}");
return 1;
}
這裡通過hashcode 來驗證是否兩個物件是否相等。
截圖如下:
然後我們再訪問一次這個介面。
上述可以得出,每一個http請求將會在同一個子容器中,且同一子容器獲取的註冊物件相同。而單例每次都相同。
transient 則是每次獲取到不同的物件。
好了,現在生命週期明瞭了,下面看一下注冊的方式。
我們通過這種方式進行註冊:
services.AddSingleton<IMySingletonService, MySingletonService>();
那麼是否還有其他方式?
還可以這樣:
services.AddSingleton<IMySingletonService>(new MySingletonService());
services.AddSingleton<IMySingletonService>(ServiceProvider =>
{
return new MySingletonService();
});
為什麼需要這樣呢?道理也是很簡單的,因為我們可能需要一些自己定製的動態引數啊。
比如說:
services.AddSingleton<IMySingletonService>(ServiceProvider =>
{
return new MySingletonService();
});
這種工廠模式,完全可以通過其他的註冊服務(ServiceProvider)的引數來例項化我們的MySingletonService。
還有值得注意的是,因為我們可以多次注入。那麼我們按道理可以獲取到註冊的全部。可以通過這種方式。
比如說:
services.AddSingleton<IMySingletonService, MySingletonService>();
services.AddSingleton<IMySingletonService>(new MySingletonService());
services.AddSingleton<IMySingletonService>(ServiceProvider =>
{
return new MySingletonService();
});
通過AddSingleton呼叫了3次。
[HttpGet]
public int GetService([FromServices] IEnumerable<IMySingletonService> mySingleton1)
{
foreach (var item in mySingleton1)
{
Console.WriteLine(item.GetHashCode());
}
return 1;
}
然後獲取一下。
發現可以一對多了,分別獲取到3個不同註冊的。
那麼如果我們的邏輯比較複雜,可能會多次呼叫到怎麼破?
services.AddSingleton<IMySingletonService, MySingletonService>();
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMySingletonService, MySingletonService>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMySingletonService, MySingletonService>());
可以通過這種方式呼叫,如果註冊資訊相同就不註冊,如果不同就註冊。
這樣就只有一個。
如果你只想註冊一個的話可以這樣:
services.AddSingleton<IMySingletonService, MySingletonService>();
services.TryAddSingleton<IMySingletonService, MySingletonService>();
services.TryAddSingleton<IMySingletonService, MySingletonService>();
這個就是如果有註冊資訊,就不再註冊了。
還有其他的如替換註冊資訊,移除註冊資訊等,可以參考官網。
然後可以關注一下這個using Microsoft.Extensions.DependencyInjection.Extensions;,這裡面是一些擴充套件的,如有自己需要的一般都在這裡面了。
另一個值得注意的地方就是泛型模板的註冊。
public interface IGeneticService<T>
{
public T getData();
}
public class GeneticService<T>: IGeneticService<T>
{
public T Data { get; private set; }
public GeneticService(T data)
{
this.Data = data;
}
public T getData()
{
return Data;
}
}
那麼通過這種註冊:
services.AddSingleton(typeof(IGeneticService<>),typeof(GeneticService<>));
然後實現一個介面:
[HttpGet]
public int GetService([FromServices] IMySingletonService mySingletonService, [FromServices] IGeneticService<IMySingletonService> geneticService)
{
Console.WriteLine(geneticService.getData().GetHashCode());
Console.WriteLine(mySingletonService.GetHashCode());
return 1;
}
結
這裡埋了一個點:
services.AddSingleton<IMySingletonService, MySingletonService>();
services.AddSingleton<IMySingletonService>(new MySingletonService());
services.AddSingleton<IMySingletonService>(ServiceProvider =>
{
return new MySingletonService();
});
那就是上面這三種註冊方式的物件釋放行為是不一樣的,如果不瞭解的話,那麼可能你的服務跑著跑著記憶體就跑高了。下一節將會解釋到。
上述只是個人整理,如有錯誤,望請指出,謝謝,一天一更。上述只是應用,如程式碼原理,請檢視<<重新整理.net core 1400篇>>。