ASP.NET Core - 依賴注入

  考慮到主題問題,在這裏不打算詳細講解依賴注入的概念,須要瞭解依賴注入的能夠關注個人DI&IoC分類講解,這裏咱們專一於ASP.NET Core 體系中系統自帶的原生IoC容器是如何讓咱們實現注入和解析的。html

  服務的生命週期  

  在開始以前,咱們先了解一下服務的生命週期,這僅涉及到IServiceCollectionIServiceProvider兩個核心對象,這也是咱們開篇文章中闡述的兩個重要對象。app

  在.NET Core中DI的核心分爲兩個組件:IServiceCollection和 IServiceProvider。框架

  • IServiceCollection               負責註冊
  • IServiceProvider                  負責提供實例
  • ActivatorUtilities(暫不講解)      負責提供實例,容許在依賴關係注入容器中建立沒有服務註冊的對象。

  服務註冊:

      public void ConfigureServices(IServiceCollection services)
        {
             services.AddTransient<ITransientTest, TransientTest>(); services.AddSingleton<ISingletonTest, SingletonTest>(); services.AddScoped<IScopedTest, ScopedTest>(); ..... }

  經過IServiceCollection這個對象,系統將相應的服務以不一樣的生命週期模式(Transient、Scoped和Singleton)註冊到ServiceCollection對象裏面。 ide

  在此須要解釋一下服務的生命週期源碼分析

  Singleton:整個應用程序生命週期內只建立一個實例 ui

  Transient:每一次請求都會建立一個新的實例this

  Scoped:  每次從同一個容器中獲取的實例是相同的、  spa

interface ITransientTest { }
interface ISingletonTest { }
interface IScopedTest { }

class TransientTest : ITransientTest { }
class SingletonTest : ISingletonTest { }
class ScopedTest : IScopedTest { }

class Program
{
    static void Main(string[] args)
    {
        IServiceCollection services = new ServiceCollection();
        services = services.AddTransient<ITransientTest, TransientTest>();
        services = services.AddScoped<IScopedTest, ScopedTest>();
        services = services.AddSingleton<ISingletonTest, SingletonTest>();

        IServiceProvider serviceProvider = services.BuildServiceProvider();
         
        Console.WriteLine(ReferenceEquals(serviceProvider.GetService<ITransientTest>(), serviceProvider.GetService<ITransientTest>()));
        Console.WriteLine(ReferenceEquals(serviceProvider.GetService<IScopedTest>(), serviceProvider.GetService<IScopedTest>()));
        Console.WriteLine(ReferenceEquals(serviceProvider.GetService<ISingletonTest>(), serviceProvider.GetService<ISingletonTest>()));

        IServiceProvider serviceProvider1 = serviceProvider.CreateScope().ServiceProvider;
        IServiceProvider serviceProvider2 = serviceProvider.CreateScope().ServiceProvider;

        Console.WriteLine(ReferenceEquals(serviceProvider1.GetService<IScopedTest>(), serviceProvider1.GetService<IScopedTest>()));
        Console.WriteLine(ReferenceEquals(serviceProvider1.GetService<IScopedTest>(), serviceProvider2.GetService<IScopedTest>()));
        Console.WriteLine(ReferenceEquals(serviceProvider1.GetService<ISingletonTest>(), serviceProvider2.GetService<ISingletonTest>()));

        /* False
         * True
         * True
         * True
         * False
         * True
         */
    }
}

  對象解析:

    當咱們須要從容器中解析一個對象出來的時候,用到了IServiceProvider對象,它根據IServiceCollection註冊的服務類型提取相應的服務對象。  code

public class Test
{
    private readonly IServiceProvider _serviceProvider;
public Test(IServicCollection serviceCollection) { _serviceProvider = serviceCollection.BuildServiceProvider(); }   public TestController()   {   //若是沒有,則返回null   var testService1 = _serviceProvider.GetService<ITestService>();   //若是沒有,則拋出InvalidOperationException異常   var testService2 = _serviceProvider.GetRequiredService<ITestService>();   //獲取對應的集合   var testService3 = _serviceProvider.GetServices<ITestService>();     var testService4 = _serviceProvider.GetRequiredServices<ITestService>();   }  }

 

  總體的一個依賴注入流程以下圖htm

    

 

  追本溯源

  在上面咱們知道了如何經過IServiceCollection和IServiceProvider這兩個對象註冊和解析對象,那麼問題來了,這兩個對象是何時和如何建立的呢?

  回到咱們ASP.NET Core - 從Program和Startup開始的控制檯程序,在WebHostBuilder調用Build方法建立WebHost的過程當中,這時會建立一個ServiceCollection對象,並將系統須要的一系列預約義服務(如IHostingEnvironment、IConfiguration、IHttpContextFactory、IStartupFilter等)註冊在它之上。接下來會利用這個ServiceCollection對象建立出對應的ServieProvider(BuildServiceProvider()),而這兩個ServiceProvider和ServiceCollection對象會一併傳遞給最終建立的WebHost,WebHost會利用這個ServiceProvider對象解析出Startup對象,並調用它的Configure方法用來完成對整個管道的創建。

  很是值得一提的是,Startup中的ConfigureServices方法是容許具備一個IServiceProvider類型的返回值,若是這個方法返回一個具體的ServiceProrivder,那麼WebHostBuilder將不會利用ServiceCollection來建立新的ServiceProvider,而是直接使用這個返回的ServiceProvider來傳遞到Startup的Configure方法以供使用。咱們後面會基於這個特性使用其餘的IoC框架進行擴展替換。

   這是WebHost調用Startup類的Configure方法建立管道的過程

public void Initialize()
{
    _startup = _hostingServiceProvider.GetService<IStartup>();
    _applicationServices = _startup.ConfigureServices(_applicationServiceCollection);
    EnsureServer();
    var builderFactory = _applicationServices.GetRequiredService<IApplicationBuilderFactory>();
    var builder = builderFactory.CreateBuilder(Server.Features);
    builder.ApplicationServices = _applicationServices;

    var startupFilters = _applicationServices.GetService<IEnumerable<IStartupFilter>>();
    Action<IApplicationBuilder> configure = _startup.Configure;
    foreach (var filter in startupFilters.Reverse())
    {
        configure = filter.Configure(configure);
    }
    configure(builder);

    this._application = builder.Build();   
}

   在這裏咱們再也不深究一個完整的流程的每一個階段,後面源碼分析的時候會結合整個流程展現。

相關文章
相關標籤/搜索