Asp.net Core 启动流程分析


新建的.net core 程序启动本质上是一个控制台应用程序,所以它的入口在Main方法中,所以启动的开始时从Main方法开始。
 1     public class Program
 2     {
 3         public static void Main(string[] args)
 4         {
 5             BuildWebHost(args).Run();
 6         }
 7 
 8         public static IWebHost BuildWebHost(string[] args)=>
 9             WebHost.CreateDefaultBuilder(args).UseStartup<Startup>().Build();
10 
11     }

WebHost的CreateDefaultBuilder方法负责创建WebHostBuilder,最后调用WebHostBuilder的build方法创建一个WebHost,这个流程是现在Core里面流行的创建方式,类似读取Config的流程。

IConfiguration configuration = new ConfigurationBuilder().Add(new MemoryConfigurationSource { InitialData = source }).Build(); configuration.GetSection("Format");//调用Config的获得数据的方法。

我们看到的第一个方法:WebHost.CreateDefaultBuilder(args)。

 1  public static IWebHostBuilder CreateDefaultBuilder(string[] args)
 2         {
 3             var builder = new WebHostBuilder()
 4                 .UseKestrel()//用Kestreal作为服务器
 5                 .UseContentRoot(Directory.GetCurrentDirectory())//设置程序的根目录
 6                 .ConfigureAppConfiguration((hostingContext, config) =>
 7                 {
 8                     //根据环境把对应的appsetting配置文件加入到config容器中
 9                     var env = hostingContext.HostingEnvironment;
10 
11                     config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
12                           .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
13 
14                     if (env.IsDevelopment())
15                     {
16                         var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
17                         if (appAssembly != null)
18                         {
19                             //添加用户机密数据
20                             config.AddUserSecrets(appAssembly, optional: true);
21                         }
22                     }
23                     //添加环境变量
24                     config.AddEnvironmentVariables();
25                     //这句加参数不知道有什么用
26                     if (args != null)
27                     {
28                         config.AddCommandLine(args);
29                     }
30                 })
31                 .ConfigureLogging((hostingContext, logging) =>
32                 {
33                     logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
34                     logging.AddConsole();
35                     logging.AddDebug();
36                 })
37                 .UseIISIntegration()
38                 .UseDefaultServiceProvider((context, options) =>
39                 {
40                     options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
41                 });
42 
43             return builder;
44         }

添加用户机密是干什么的可以详细看ASP.NET Core 优雅的在开发环境保存机密(User Secrets),具体就是用来保存一些机密数据。

WebHostBuilder类

 1         //环境变量的参数
 2         private readonly IHostingEnvironment _hostingEnvironment;
 3         //存储添加的Service委托的一个集合
 4         private readonly List<Action<WebHostBuilderContext, IServiceCollection>> _configureServicesDelegates;
 5         //访问配置文件
 6         private IConfiguration _config;
 7         //WebHost的一些配置选项,它里面其实都是存在config里面的
 8         private WebHostOptions _options;
 9         //webHostBuilder上下文,里面两个属性,一个是IHostingEnvironment,一个是IConfiguration
10         private WebHostBuilderContext _context;
11         //判断是否调用了Build方法,调用以后为true,再次调用Build方法时会报错
12         private bool _webHostBuilt;
13         //存储添加配置的一个委托集合
14         private List<Action<WebHostBuilderContext, IConfigurationBuilder>> _configureAppConfigurationBuilderDelegates;
15 
16         /// <summary>
17         /// Initializes a new instance of the <see cref="WebHostBuilder"/> class.
18         /// </summary>
19         public WebHostBuilder()
20         {
21             _hostingEnvironment = new HostingEnvironment();
22             _configureServicesDelegates = new List<Action<WebHostBuilderContext, IServiceCollection>>();
23             _configureAppConfigurationBuilderDelegates = new List<Action<WebHostBuilderContext, IConfigurationBuilder>>();
24 
25             _config = new ConfigurationBuilder()
26                 .AddEnvironmentVariables(prefix: "ASPNETCORE_")
27                 .Build();
28 
29             if (string.IsNullOrEmpty(GetSetting(WebHostDefaults.EnvironmentKey)))
30             {
31                 // Try adding legacy environment keys, never remove these.
32                 UseSetting(WebHostDefaults.EnvironmentKey, Environment.GetEnvironmentVariable("Hosting:Environment")
33                     ?? Environment.GetEnvironmentVariable("ASPNET_ENV"));
34             }
35 
36             if (string.IsNullOrEmpty(GetSetting(WebHostDefaults.ServerUrlsKey)))
37             {
38                 // Try adding legacy url key, never remove this.
39                 UseSetting(WebHostDefaults.ServerUrlsKey, Environment.GetEnvironmentVariable("ASPNETCORE_SERVER.URLS"));
40             }
41 
42             _context = new WebHostBuilderContext
43             {
44                 Configuration = _config
45             };
46         }

ConfigureAppConfiguration方法就是把委托加到_configureAppConfigurationBuilderDelegates的集合当中去。

AddLogging就是把Service添加到_configureServicesDelegates中去。

UseStartUp方法

        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));
                        });
                    }
                });
        }

设置一下webhostBuilder的程序集名称,把StartUp注册到DI容器中去。目前还没有注入到di容器中去,只是跟之前一样,把这个委托加到了_configureServicesDelegates中去了,在循环执行委托的时候,才真正的注入到di容器中去,这个委托的用处就是把StartUp注入到DI去。

此时有两个判断,如果泛型传进来的StartUp类是继承了IStartup的话就直接注入到DI去的,如果不是的话,就装载默认的StartUp类,也就是我们Core项目的新建时候的那个StartUp类。

看一下StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName)方法的代码:

 1         public static StartupMethods LoadMethods(IServiceProvider hostingServiceProvider, Type startupType, string environmentName)
 2         {
 3             //获取StartUp类的Config方法(注:这个是可以根据环境来获取不同的配置方法:方法名带有环境变量类似于:Configure{0},占位符就是环境变量的值test,stage等)
 4             var configureMethod = FindConfigureDelegate(startupType, environmentName);
 5             //获取StartUp类的ConfigService方法(跟Config获取一样可以分环境获取方法,Configure{0}Service)
 6             var servicesMethod = FindConfigureServicesDelegate(startupType, environmentName);
 7 
 8             //获取StartUp类的ConfigContainer方法(跟Config获取一样可以分环境获取方法,Configure{0}Container)
 9             var configureContainerMethod = FindConfigureContainerDelegate(startupType, environmentName);
10 
11             object instance = null;
12             if (!configureMethod.MethodInfo.IsStatic || (servicesMethod != null && !servicesMethod.MethodInfo.IsStatic))
13             {
14                 instance = ActivatorUtilities.GetServiceOrCreateInstance(hostingServiceProvider, startupType);
15             }
16             //构建一个执行ConfigService的委托
17             var configureServicesCallback = servicesMethod.Build(instance);
18             var configureContainerCallback = configureContainerMethod.Build(instance);
19 
20             Func<IServiceCollection, IServiceProvider> configureServices = services =>
21             {
22                 // Call ConfigureServices, if that returned an IServiceProvider, we're done,执行ConfigService,如果执行ConfigService方法返回的是一个IServiceProvider,则就直接返回,不执行下面的替换ConfigContainer的方法
23                 IServiceProvider applicationServiceProvider = configureServicesCallback.Invoke(services);
24 
25                 if (applicationServiceProvider != null)
26                 {
27                     return applicationServiceProvider;
28                 }
29 
30                 // If there's a ConfigureContainer method
31                 if (configureContainerMethod.MethodInfo != null)
32                 {
33                     // We have a ConfigureContainer method, get the IServiceProviderFactory<TContainerBuilder>
34                     var serviceProviderFactoryType = typeof(IServiceProviderFactory<>).MakeGenericType(configureContainerMethod.GetContainerType());
35                     var serviceProviderFactory = hostingServiceProvider.GetRequiredService(serviceProviderFactoryType);
36                     // var builder = serviceProviderFactory.CreateBuilder(services);
37                     var builder = serviceProviderFactoryType.GetMethod(nameof(DefaultServiceProviderFactory.CreateBuilder)).Invoke(serviceProviderFactory, new object[] { services });
38                     configureContainerCallback.Invoke(builder);
39                     // applicationServiceProvider = serviceProviderFactory.CreateServiceProvider(builder);
40                     applicationServiceProvider = (IServiceProvider)serviceProviderFactoryType.GetMethod(nameof(DefaultServiceProviderFactory.CreateServiceProvider)).Invoke(serviceProviderFactory, new object[] { builder });
41                 }
42                 else
43                 {
44                     // Get the default factory
45                     var serviceProviderFactory = hostingServiceProvider.GetRequiredService<IServiceProviderFactory<IServiceCollection>>();
46 
47                     // Don't bother calling CreateBuilder since it just returns the default service collection
48                     applicationServiceProvider = serviceProviderFactory.CreateServiceProvider(services);
49                 }
50 
51                 return applicationServiceProvider ?? services.BuildServiceProvider();
52             };
53 
54             return new StartupMethods(instance, configureMethod.Build(instance), configureServices);
55         }
ConventionBasedStartup这个类就是一个类似于StartUp的类,New得到时候,就是把StartupMethods传进去,里面的话,就是有两个方法Config指向StartUp的Config方法,ConfigService方法执行StartUp的ConfigService方法。

 

最后就是WebHostBuilder.Build()方法

 

 1 public IWebHost Build()
 2         {
 3             if (_webHostBuilt)
 4             {
 5                 throw new InvalidOperationException(Resources.WebHostBuilder_SingleInstance);
 6             }
 7             _webHostBuilt = true;
 8 
 9             // Warn about deprecated environment variables
10             if (Environment.GetEnvironmentVariable("Hosting:Environment") != null)
11             {
12                 Console.WriteLine("The environment variable 'Hosting:Environment' is obsolete and has been replaced with 'ASPNETCORE_ENVIRONMENT'");
13             }
14 
15             if (Environment.GetEnvironmentVariable("ASPNET_ENV") != null)
16             {
17                 Console.WriteLine("The environment variable 'ASPNET_ENV' is obsolete and has been replaced with 'ASPNETCORE_ENVIRONMENT'");
18             }
19 
20             if (Environment.GetEnvironmentVariable("ASPNETCORE_SERVER.URLS") != null)
21             {
22                 Console.WriteLine("The environment variable 'ASPNETCORE_SERVER.URLS' is obsolete and has been replaced with 'ASPNETCORE_URLS'");
23             }
24 
25             var hostingServices = BuildCommonServices(out var hostingStartupErrors);
26             var applicationServices = hostingServices.Clone();
27             var hostingServiceProvider = hostingServices.BuildServiceProvider();
28 
29             AddApplicationServices(applicationServices, hostingServiceProvider);
30 
31             var host = new WebHost(
32                 applicationServices,
33                 hostingServiceProvider,
34                 _options,
35                 _config,
36                 hostingStartupErrors);
37 
38             host.Initialize();
39 
40             return host;
41         }

 

里面的重点是在BuildCommonServices中,里面默认添加了一系列的Service。如下面的代码:

  1         private IServiceCollection BuildCommonServices(out AggregateException hostingStartupErrors)
  2         {
  3             hostingStartupErrors = null;
  4 
  5             _options = new WebHostOptions(_config);
  6 
  7             if (!_options.PreventHostingStartup)
  8             {
  9                 var exceptions = new List<Exception>();
 10 
 11                 // Execute the hosting startup assemblies
 12                 // HostingStartupAssemblies = $"{ApplicationName};{configuration[WebHostDefaults.HostingStartupAssembliesKey]}".Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries) ?? new string[0];这就是HostingStartupAssembly查找,当我们有类继承与IHostingStartup是,他必须得在build的config里面添加一个key为WebHostDefaults.HostingStartupAssembliesKey的程序集名称,多个以分号隔开。
 13                 foreach (var assemblyName in _options.HostingStartupAssemblies)
 14                 {
 15                     try
 16                     {
 17                         var assembly = Assembly.Load(new AssemblyName(assemblyName));
 18 
 19                         //程序集上必须打上HostingStartupAttribute标签
 20                         foreach (var attribute in assembly.GetCustomAttributes<HostingStartupAttribute>())
 21                         {
 22                             var hostingStartup = (IHostingStartup)Activator.CreateInstance(attribute.HostingStartupType);
 23                             //执行hostingStartup的config方法。
 24                             hostingStartup.Configure(this);
 25                         }
 26                     }
 27                     catch (Exception ex)
 28                     {
 29                         // Capture any errors that happen during startup
 30                         exceptions.Add(new InvalidOperationException($"Startup assembly {assemblyName} failed to execute. See the inner exception for more details.", ex));
 31                     }
 32                 }
 33 
 34                 if (exceptions.Count > 0)
 35                 {
 36                     hostingStartupErrors = new AggregateException(exceptions);
 37                 }
 38             }
 39 
 40             var contentRootPath = ResolveContentRootPath(_options.ContentRootPath, AppContext.BaseDirectory);
 41             var applicationName = _options.ApplicationName;
 42 
 43             // Initialize the hosting environment
 44             _hostingEnvironment.Initialize(applicationName, contentRootPath, _options);
 45             _context.HostingEnvironment = _hostingEnvironment;
 46 
 47             var services = new ServiceCollection();
 48             services.AddSingleton(_hostingEnvironment);
 49             services.AddSingleton(_context);
 50 
 51             var builder = new ConfigurationBuilder()
 52                 .SetBasePath(_hostingEnvironment.ContentRootPath)
 53                 .AddInMemoryCollection(_config.AsEnumerable());
 54 
 55             //执行之前添加的配置文件的委托
 56             foreach (var configureAppConfiguration in _configureAppConfigurationBuilderDelegates)
 57             {
 58                 configureAppConfiguration(_context, builder);
 59             }
 60 
 61             var configuration = builder.Build();
 62             services.AddSingleton<IConfiguration>(configuration);
 63             _context.Configuration = configuration;
 64 
 65             var listener = new DiagnosticListener("Microsoft.AspNetCore");
 66             services.AddSingleton<DiagnosticListener>(listener);
 67             services.AddSingleton<DiagnosticSource>(listener);
 68 
 69             services.AddTransient<IApplicationBuilderFactory, ApplicationBuilderFactory>();
 70             services.AddTransient<IHttpContextFactory, HttpContextFactory>();
 71             services.AddScoped<IMiddlewareFactory, MiddlewareFactory>();
 72             services.AddOptions();
 73             services.AddLogging();
 74 
 75             // Conjure up a RequestServices
 76             services.AddTransient<IStartupFilter, AutoRequestServicesStartupFilter>();
 77             services.AddTransient<IServiceProviderFactory<IServiceCollection>, DefaultServiceProviderFactory>();
 78 
 79             // Ensure object pooling is available everywhere.
 80             services.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
 81             //之前再UseStartUp(string assemblyName)设置的startup类的程序集,在这之前也执行了 一段这个代码,在UseStartUp里面
 82             //UseStartUp<T>(),这个重载的方法里面是没有加入这个程序集名称的,只是设置一下UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName)
 83             if (!string.IsNullOrEmpty(_options.StartupAssembly))
 84             {
 85                 try
 86                 {
 87                     var startupType = StartupLoader.FindStartupType(_options.StartupAssembly, _hostingEnvironment.EnvironmentName);
 88 
 89                     if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo()))
 90                     {
 91                         services.AddSingleton(typeof(IStartup), startupType);
 92                     }
 93                     else
 94                     {
 95                         services.AddSingleton(typeof(IStartup), sp =>
 96                         {
 97                             var hostingEnvironment = sp.GetRequiredService<IHostingEnvironment>();
 98                             var methods = StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName);
 99                             return new ConventionBasedStartup(methods);
100                         });
101                     }
102                 }
103                 catch (Exception ex)
104                 {
105                     var capture = ExceptionDispatchInfo.Capture(ex);
106                     services.AddSingleton<IStartup>(_ =>
107                     {
108                         capture.Throw();
109                         return null;
110                     });
111                 }
112             }
113             //到这里就已经把说有的Service加到ServiceCollection
114             foreach (var configureServices in _configureServicesDelegates)
115             {
116                 configureServices(_context, services);
117             }
118 
119             return services;
120         }

 

 

var applicationServices = hostingServices.Clone();
var hostingServiceProvider = hostingServices.BuildServiceProvider();

AddApplicationServices(applicationServices, hostingServiceProvider)

这里就是新建一个IServiceCollection,把hostingServices里面的service复制一份到applicationService里面去,同时里面有一个

            var listener = new DiagnosticListener("Microsoft.AspNetCore");
            services.AddSingleton<DiagnosticListener>(listener);
            services.AddSingleton<DiagnosticSource>(listener);

 

这个listener,要保证在两个container里面都是同一个。
 1         private void AddApplicationServices(IServiceCollection services, IServiceProvider hostingServiceProvider)
 2         {
 3             // We are forwarding services from hosting contrainer so hosting container
 4             // can still manage their lifetime (disposal) shared instances with application services.
 5             // NOTE: This code overrides original services lifetime. Instances would always be singleton in
 6             // application container.
 7             var listener = hostingServiceProvider.GetService<DiagnosticListener>();
 8             services.Replace(ServiceDescriptor.Singleton(typeof(DiagnosticListener), listener));
 9             services.Replace(ServiceDescriptor.Singleton(typeof(DiagnosticSource), listener));
10         }

 

最后是新建一个WebHost,然后是调用Initialize方法进行初始化的一些工作,我们最重要的就是看里面的初始化干了什么东西?

 
                //确保是否_applicationService是不是空的,空的话就调用IStartUp的ConfigServices方法获得
                EnsureApplicationServices();
                //调用了UseUrls,则这里面把Server.Feature.Address的值赋值上去
                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;
                //查找是否 注册了StartUpFilter,如果注册了,则先调用过滤器
                foreach (var filter in startupFilters.Reverse())
                {
                    configure = filter.Configure(configure);
                }
                //调用第一个注册的config的方法然后接连调用链条
                configure(builder);

                return builder.Build();

EnsureApplicationServices()的方法是最终调用StartUp类里面的ConfigService方法,把Service注入进去。

private void EnsureApplicationServices()
        {
            if (_applicationServices == null)
            {
                EnsureStartup();
//调用ConfigureServices方法把服务注册进去(实际调用的是
ConventionBasedStartup这个类的ConfigService方法。
_applicationServices
= _startup.ConfigureServices(_applicationServiceCollection);
}
}

 

 

 

至此,build方法弄完了。

最后是Run方法:

                //这里时开始的方法。
await host.StartAsync(token); var hostingEnvironment = host.Services.GetService<IHostingEnvironment>(); var applicationLifetime = host.Services.GetService<IApplicationLifetime>(); Console.WriteLine($"Hosting environment: {hostingEnvironment.EnvironmentName}"); Console.WriteLine($"Content root path: {hostingEnvironment.ContentRootPath}");
//UseUrls时,传入的url,这里回监听这些地址。
var serverAddresses = host.ServerFeatures.Get<IServerAddressesFeature>()?.Addresses; if (serverAddresses != null) { foreach (var address in serverAddresses) { Console.WriteLine($"Now listening on: {address}"); } } if (!string.IsNullOrEmpty(shutdownMessage)) { Console.WriteLine(shutdownMessage); } await host.WaitForTokenShutdownAsync(token); }

 

StartAsync里面最终调用的就是IServer的Start方法。

 

最终的启动流程就结束了!!!!。

 

最后感觉,这个启动流程为了配合DI容器写的好绕,里面很多的委托进行传进传出,看的人很晕!!

但是它带来的灵活性也是巨大的,可以自定义很多地方,真正体现了万物皆DI的思想! 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM