0五、NetCore2.0依赖注入(DI)之Web应用启动流程管理

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接口进行注入的,并且是单例模式。

至此一个简单网站的初始化过程咱们就基本清楚了!

相关文章
相关标签/搜索