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


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

    在一个Asp.net core 2.0 Web应用程序中,启动过程都做了些什么?NetCore2.0的依赖注入(DI)框架是如何管理启动过程的?WebServer和Startup是如何注册的?

    ------------------------------------------------------------------------------------------------------------

     写在前面:这是一个系列的文章,总目录请移步:NetCore2.0技术文章目录

    ------------------------------------------------------------------------------------------------------------

    一、我们先看看依赖注入框架是如何使用的

    NetCore2.0的依赖注入(DI)框架是要解决对象创建的问题,把创建对象与使用对象进行解耦。调用者不需要关心对象是单例的还是多实例的;服务的扩展和调用也更容易。

    首先使用VS2017新建一个控制台程序,要使用依赖注入(DI)框架,我们需要引入微软的依赖注入包:

    install-package Microsoft.Extensions.DependencyInjection

    我们声明一个自己的接口,并实现一个类

    // 接口
    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业务的调用方,不需要关心是如何实例化的。

    二、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

    三、其中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接口进行注入的,而且是单例模式。

    四、不难推断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接口进行注入的,而且是单例模式。

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

    【敦格原创】欢迎引用,请注明来源:http://www.cnblogs.com/netcore2
  • 相关阅读:
    解决一切日期问题的日期类
    汉诺塔类型问题解析
    窗口滑动
    大暑假集训总结(反思)
    找硬币题解
    Fiolki题解
    大逃亡题解
    Luogu6080 [USACO05DEC]Cow Patterns G
    Luogu3193 HNOI2008 GT考试
    Codeforces1355F Guess Divisors Count
  • 原文地址:https://www.cnblogs.com/netcore2/p/7417053.html
Copyright © 2020-2023  润新知