0五、NetCore2.0依賴注入(DI)之Web應用啓動流程管理html
在一個Asp.net core 2.0 Web應用程序中,啓動過程都作了些什麼?NetCore2.0的依賴注入(DI)框架是如何管理啓動過程的?WebServer和Startup是如何註冊的?git
------------------------------------------------------------------------------------------------------------github
寫在前面:這是一個系列的文章,總目錄請移步:NetCore2.0技術文章目錄app
------------------------------------------------------------------------------------------------------------框架
1、咱們先看看依賴注入框架是如何使用的ide
NetCore2.0的依賴注入(DI)框架是要解決對象建立的問題,把建立對象與使用對象進行解耦。調用者不須要關心對象是單例的仍是多實例的;服務的擴展和調用也更容易。網站
首先使用VS2017新建一個控制檯程序,要使用依賴注入(DI)框架,咱們須要引入微軟的依賴注入包:ui
install-package Microsoft.Extensions.DependencyInjectionthis
咱們聲明一個本身的接口,並實現一個類spa
// 接口 interface IRun { void Run(); } // 實現類 class Run : IRun { void IRun.Run() { Console.WriteLine("跑起來,兄弟"); } }
使用DI框架來註冊接口和類的實例;並經過服務提供者來訪問接口
using Microsoft.Extensions.DependencyInjection; using System; namespace MyServiceBus { class Program { static void Main(string[] args) { // 實例化DI框架 IServiceCollection services = new ServiceCollection(); // 在DI框架中加入接口的一個實例(它是單例的) services.AddSingleton<IRun, Run>(); // 服務的提供者 IServiceProvider serviceProvider = services.BuildServiceProvider(); // ============上下兩部分代碼通常不會同時出如今一個類中======== // 從服務提供者獲取接口的實例(不用關心是如何建立的) serviceProvider.GetService<IRun>().Run(); Console.ReadLine(); } } }
看看運行效果吧!能夠看出,IRun業務的調用方,不須要關心是如何實例化的。
2、DI框架如何管理Asp.NetCore2.0 Web應用的啓動過程
一個極簡的Web應用程序通常是這樣的:
using Microsoft.AspNetCore.Hosting; namespace MyWebApi { class Program { static void Main(string[] args) { var host = new WebHostBuilder() .UseKestrel() .UseStartup<StartUp>() .Build(); host.Run(); } } }
從上面的代碼中判斷,DI框架的初始化和接口註冊應該是在WebHostBuild.Build()方法中完成的,從命名就能看出,這是一個建造者模式,把內部複雜的構建方式隱藏了。咱們去看一下這個方法的開源代碼:
// 爲了說明問題,代碼略做調整,保留核心代碼
public IWebHost Build() { // 初始化DI框架:估計裏面預製了一些服務 IServiceCollection hostingServices = BuildCommonServices(out var hostingStartupErrors); IServiceCollection applicationServices = hostingServices.Clone();
// 服務的提供者 ServiceProvider hostingServiceProvider = hostingServices.BuildServiceProvider(); AddApplicationServices(applicationServices, hostingServiceProvider); var host = new WebHost( applicationServices, hostingServiceProvider, _options, _config, hostingStartupErrors); host.Initialize(); return host; }
咱們能夠看到在WebHostBuild.Build()方法中,顯示的初始化了DI框架,咱們看一下DI框架初始化方法的源碼,能夠發現確實預製了一些服務:
// 爲了說明問題,代碼略做調整,保留核心代碼 private IServiceCollection BuildCommonServices(out AggregateException hostingStartupErrors) { // 配置選項 _options = new WebHostOptions(_config); var contentRootPath = ResolveContentRootPath(_options.ContentRootPath, AppContext.BaseDirectory); var applicationName = _options.ApplicationName; // Initialize the hosting environment _hostingEnvironment.Initialize(applicationName, contentRootPath, _options); _context.HostingEnvironment = _hostingEnvironment; // 實例化DI框架 var services = new ServiceCollection(); // 預製環境參數服務到框架中 services.AddSingleton(_hostingEnvironment); // 預製上下文插件到框架中 services.AddSingleton(_context); // 預製配置管理服務到框架中 var builder = new ConfigurationBuilder() .SetBasePath(_hostingEnvironment.ContentRootPath) .AddInMemoryCollection(_config.AsEnumerable()); var configuration = builder.Build(); services.AddSingleton<IConfiguration>(configuration); _context.Configuration = configuration; // 預製診斷服務到框架中 var listener = new DiagnosticListener("Microsoft.AspNetCore"); services.AddSingleton<DiagnosticListener>(listener); services.AddSingleton<DiagnosticSource>(listener); // 預製其餘服務到框架中 services.AddTransient<IApplicationBuilderFactory, ApplicationBuilderFactory>(); services.AddTransient<IHttpContextFactory, HttpContextFactory>(); services.AddScoped<IMiddlewareFactory, MiddlewareFactory>(); services.AddOptions(); services.AddLogging(); // Conjure up a RequestServices services.AddTransient<IStartupFilter, AutoRequestServicesStartupFilter>(); services.AddTransient<IServiceProviderFactory<IServiceCollection>, DefaultServiceProviderFactory>(); // Ensure object pooling is available everywhere. services.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>(); return services; }
從代碼分析看WebHostBuilder作的事以下:
1. 定義了一些WebHost的配置項
2. 建立依賴注入的容器, 並預製一些service
3、其中WebServer就是做爲服務進行注入的
回頭再看WebServer的註冊,使用的是UseKestrel:
using Microsoft.AspNetCore.Hosting; namespace MyWebApi { class Program { static void Main(string[] args) { var host = new WebHostBuilder() .UseKestrel() .UseStartup<StartUp>() .Build(); host.Run(); } } }
猜想也是做爲服務進行注入的,咱們來看Kestrel的開源代碼:
public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder) { hostBuilder.UseLibuv(); return hostBuilder.ConfigureServices(services => { services.AddTransient<IConfigureOptions<KestrelServerOptions>, KestrelServerOptionsSetup>(); services.AddSingleton<IServer, KestrelServer>(); }); }
從中可以看出WebServer是做爲IServer接口進行注入的,並且是單例模式。
4、不難推斷Startup也是做爲服務進行注入的
極簡的Web應用程序通常是這樣的:
using Microsoft.AspNetCore.Hosting; namespace MyWebApi { class Program { static void Main(string[] args) { var host = new WebHostBuilder() .UseKestrel() .UseStartup<StartUp>() .Build(); host.Run(); } } }
咱們來看開源代碼:
public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType) { var startupAssemblyName = startupType.GetTypeInfo().Assembly.GetName().Name; return hostBuilder .UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName) .ConfigureServices(services => { if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo())) { services.AddSingleton(typeof(IStartup), startupType); } else { services.AddSingleton(typeof(IStartup), sp => { var hostingEnvironment = sp.GetRequiredService<IHostingEnvironment>(); return new ConventionBasedStartup(StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName)); }); } }); }
從中可以看出Startup是做爲IStartup接口進行注入的,並且是單例模式。
至此一個簡單網站的初始化過程咱們就基本清楚了!