码迷,mamicode.com
首页 > Web开发 > 详细

ASP.NET Core 2.0 : 七.一张图看透启动背后的秘密

时间:2018-03-22 10:56:36      阅读:217      评论:0      收藏:0      [点我收藏+]

标签:assembly   文件   alt   defaults   ensure   virt   env   分享图片   tor   

  为什么我们可以在Startup这个 “孤零零的” 类中配置依赖注入和管道?

  它是什么时候被实例化并且调用的?

  参数中的IServiceCollection services是怎么来的?

  处理管道是怎么构建起来的?

  启动过程中,系统“默默的”做了哪些准备工作?

  上一篇文章讲了ASP.NET Core中的依赖注入(系列目录), 而它的配置是在Startup这个文件中的 ConfigureServices(IServiceCollection services) 方法,而且Startup这个类也没有继承任何类或者接口。 深入的想一想,可能会冒出类似上面列出的好多问题,下面用一幅图来看透它。

一、整体流程图

先上图, 觉得看不清可以点击看大图或者下载后放大查看。

技术分享图片

图一  (点击放大

二、WebHostBuilder

  应用程序在Main方法之后通过调用Create­DefaultBuilder方法创建并配置WebHostBuilder, 

 1     public class WebHostBuilder : IWebHostBuilder
 2     {
 3         private readonly List<Action<WebHostBuilderContext, IServiceCollection>> _configureServicesDelegates;
 4 
 5         private IConfiguration _config;
 6         public IWebHostBuilder UseSetting(string key, string value)
 7         {
 8             _config[key] = value;
 9             return this;
10         }
22         public IWebHostBuilder ConfigureServices(Action<WebHostBuilderContext, IServiceCollection> configureServices)
23         {
24             if (configureServices == null)
25             {
26                 throw new ArgumentNullException(nameof(configureServices));
27             }
29             _configureServicesDelegates.Add(configureServices);
30             return this;
31         }
32     }

WebHostBuilder存在一个重要的集合 private readonly List<Action<WebHostBuilderContext, IServiceCollection>> _configureServicesDelegates; , 通过 ConfigureServices 方法将需要的Action加入进来。

UseSetting是一个用于设置Key-Value的方法, 一些常用的配置均会通过此方法写入_config中。

三、UseStartup<Startup>()

  Create­DefaultBuilder之后调用UseStartup<Startup>(),指定Startup为启动类。

        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类对应的AssemblyName, 调用UseSetting方法将其设置为WebHostDefaults.ApplicationKey(“applicationName”)的值。

然后调用WebHostBuilder的ConfigureServices方法,将一个Action写入WebHostBuilder 的 configureServicesDelegates中。

这个Action的意思就是说,如果这个被指定的类startupType是一个实现了IStartup的类, 那么将其通过AddSingleton注册到services 这个ServiceCollection中, 如果不是, 那么将其“转换”成 ConventionBasedStartup 这个实现了 IStartup的类后再进行注册。这里涉及到一个StartupLoader的LoadMethods()方法,会通过字符串的方式查找“ConfigureServices”、“Configure{ environmentName}Services”这样的方法。

注意:这里只是将一个Action写入了configureServicesDelegates, 而不是已经执行了对IStartup的注册, 因为这个Action尚未执行,services也还不存在。就像菩萨对八戒说: 八戒(Startup)你先在高老庄等着吧, 将来有个和尚带领一个取经小分队(ServiceCollection services )过来的时候你加入他们。

其实在Create­DefaultBuilder方法中的几个UseXXX的方法也是这样通过ConfigureServices将对应的Action写入了configureServicesDelegates, 等待唐僧的到来。

四、WebHostBuilder.Build()

创建并配置好的WebHostBuilder开始通过Build方法创建WebHost了, 首先是BuildCommonServices, 

 1 private IServiceCollection BuildCommonServices(out AggregateException hostingStartupErrors)
 2         {
 3             //...省略...
 4             var services = new ServiceCollection();
 5             services.AddSingleton(_hostingEnvironment);
 6             services.AddSingleton(_context);
 7             //....各种Add....
 9             foreach (var configureServices in _configureServicesDelegates)
10             {
11                 configureServices(_context, services);
12             }
14             return services;
15         }                

  在这个方法里创建了ServiceCollection services(以唐僧为首的取经小分队), 然后通过各种Add方法注册了好多内容进去(收了悟空),然后foreach 之前暂存在configureServicesDelegates中的各个Action,传入services逐一执行, 将之前需要注册的内容注册到services中, 这里就包括Startup(八戒),注意这里仅是进行了注册,而未执行Startup的方法。

  处理好的这个services被BuildCommonServices返回后赋值给 hostingServices,然后 hostingServices经过Clone()生成 applicationServices,再由这个 applicationServices进行 GetProviderFromFactory(hostingServices)生成一个 IServiceProvider hostingServiceProvider.经过一系列的处理后,可以创建WebHost了。

var host = new WebHost(
    applicationServices,
    hostingServiceProvider,
    _options,
    _config,
    hostingStartupErrors);

host.Initialize();

 将生成的applicationServices 和 hostingServiceProvider作为参数传递给新生成的WebHost。接下来就是这个WebHost的 Initialize()。

 五、WebHost.Initialize()

WebHost的 Initialize()的主要工作就是BuildApplication()。

EnsureApplicationServices(): 用来处理WebHost的 private IServiceProvider _applicationServices ,④Startup的ConfigureServices方法在这里被调用

_startup = _hostingServiceProvider.GetRequiredService<IStartup>();
_applicationServices = _startup.ConfigureServices(_applicationServiceCollection);

通过 GetRequiredService<IStartup>() 获取到我们的_startup, 然后调用这个_startup的 ⑤ConfigureServices 方法,这就是我们用于依赖注入的startup类的ConfigureServices方法了。

所以,_applicationServices是根据_applicationServiceCollection 加上我们在_startup中注册的内容之后重新生成的 IServiceProvider。

EnsureServer()通过 GetRequiredService<IServer>()获取Server并配置监听地址。

var builderFactory = _applicationServices.GetRequiredService<IApplicationBuilderFactory>();
var builder = builderFactory.CreateBuilder(Server.Features);
builder.ApplicationServices = _applicationServices;

获取到 IApplicationBuilderFactory并通过它创建 IApplicationBuilder,并将上面创建的_applicationServices赋值给它的ApplicationServices,它还有个重要的集合_components

private readonly IList<Func<RequestDelegate, RequestDelegate>> _components = new List<Func<RequestDelegate, RequestDelegate>>();

 从_components的类型可以看出它其实是中间件的集合,是该调用我们的_startup的Configure方法的时候了。

先获取定义的IStartupFilter, foreach这些IStartupFilter并与_startup的Configure方法一起将配置的中间件写入_components,然后通过 Build()创建RequestDelegate _application,

在Build()中对_components进行处理生成请求处理管道,关于IStartupFilter和生成管道这部分将在下篇文章进行详细说明。

六、WebHost.Run()

  WebHost创建完毕, 最后一步就是Run起来了,WebHost的Run()会调用它的方法StartAsync()

public virtual async Task StartAsync(CancellationToken cancellationToken = default(CancellationToken))
{
    //......var hostingApp = new HostingApplication(_application, _logger, diagnosticSource, httpContextFactory);
    await Server.StartAsync(hostingApp, cancellationToken).ConfigureAwait(false);
    _hostedServiceExecutor.StartAsync(cancellationToken).ConfigureAwait(false);
    //.....
}

  在之前的文章中我们知道,请求是经过 Server监听=>处理成httpContext=>Application处理,所以这里首先传入上面创建的_application和一个httpContextFactory来⑨生成一个HostingApplication,并将这个HostingApplication传入Server的StartAsync(), 当Server监听到请求之后, 后面的工作由HostingApplication来完成。

  ⑩hostedServiceExecutor.StartAsync()方法用来开启一个后台运行的服务,一些需要后台运行的操作比如定期刷新缓存等可以放到这里来。

ASP.NET Core 2.0 : 七.一张图看透启动背后的秘密

标签:assembly   文件   alt   defaults   ensure   virt   env   分享图片   tor   

原文地址:https://www.cnblogs.com/FlyLolo/p/ASPNETCore2_7.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!