Asp.net Core 启动流程分析
新建的.net core 程序启动本质上是一个控制台应用程序,所以它的入口在Main方法中,所以启动的开始时从Main方法开始。
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
} public static IWebHost BuildWebHost(string[] args)=>
WebHost.CreateDefaultBuilder(args).UseStartup<Startup>().Build(); }
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)。
public static IWebHostBuilder CreateDefaultBuilder(string[] args)
{
var builder = new WebHostBuilder()
.UseKestrel()//用Kestreal作为服务器
.UseContentRoot(Directory.GetCurrentDirectory())//设置程序的根目录
.ConfigureAppConfiguration((hostingContext, config) =>
{
//根据环境把对应的appsetting配置文件加入到config容器中
var env = hostingContext.HostingEnvironment; config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); if (env.IsDevelopment())
{
var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
if (appAssembly != null)
{
//添加用户机密数据
config.AddUserSecrets(appAssembly, optional: true);
}
}
//添加环境变量
config.AddEnvironmentVariables();
//这句加参数不知道有什么用
if (args != null)
{
config.AddCommandLine(args);
}
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddDebug();
})
.UseIISIntegration()
.UseDefaultServiceProvider((context, options) =>
{
options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
}); return builder;
}
添加用户机密是干什么的可以详细看ASP.NET Core 优雅的在开发环境保存机密(User Secrets),具体就是用来保存一些机密数据。
WebHostBuilder类
//环境变量的参数
private readonly IHostingEnvironment _hostingEnvironment;
//存储添加的Service委托的一个集合
private readonly List<Action<WebHostBuilderContext, IServiceCollection>> _configureServicesDelegates;
//访问配置文件
private IConfiguration _config;
//WebHost的一些配置选项,它里面其实都是存在config里面的
private WebHostOptions _options;
//webHostBuilder上下文,里面两个属性,一个是IHostingEnvironment,一个是IConfiguration
private WebHostBuilderContext _context;
//判断是否调用了Build方法,调用以后为true,再次调用Build方法时会报错
private bool _webHostBuilt;
//存储添加配置的一个委托集合
private List<Action<WebHostBuilderContext, IConfigurationBuilder>> _configureAppConfigurationBuilderDelegates; /// <summary>
/// Initializes a new instance of the <see cref="WebHostBuilder"/> class.
/// </summary>
public WebHostBuilder()
{
_hostingEnvironment = new HostingEnvironment();
_configureServicesDelegates = new List<Action<WebHostBuilderContext, IServiceCollection>>();
_configureAppConfigurationBuilderDelegates = new List<Action<WebHostBuilderContext, IConfigurationBuilder>>(); _config = new ConfigurationBuilder()
.AddEnvironmentVariables(prefix: "ASPNETCORE_")
.Build(); if (string.IsNullOrEmpty(GetSetting(WebHostDefaults.EnvironmentKey)))
{
// Try adding legacy environment keys, never remove these.
UseSetting(WebHostDefaults.EnvironmentKey, Environment.GetEnvironmentVariable("Hosting:Environment")
?? Environment.GetEnvironmentVariable("ASPNET_ENV"));
} if (string.IsNullOrEmpty(GetSetting(WebHostDefaults.ServerUrlsKey)))
{
// Try adding legacy url key, never remove this.
UseSetting(WebHostDefaults.ServerUrlsKey, Environment.GetEnvironmentVariable("ASPNETCORE_SERVER.URLS"));
} _context = new WebHostBuilderContext
{
Configuration = _config
};
}
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)方法的代码:
public static StartupMethods LoadMethods(IServiceProvider hostingServiceProvider, Type startupType, string environmentName)
{
//获取StartUp类的Config方法(注:这个是可以根据环境来获取不同的配置方法:方法名带有环境变量类似于:Configure{0},占位符就是环境变量的值test,stage等)
var configureMethod = FindConfigureDelegate(startupType, environmentName);
//获取StartUp类的ConfigService方法(跟Config获取一样可以分环境获取方法,Configure{0}Service)
var servicesMethod = FindConfigureServicesDelegate(startupType, environmentName); //获取StartUp类的ConfigContainer方法(跟Config获取一样可以分环境获取方法,Configure{0}Container)
var configureContainerMethod = FindConfigureContainerDelegate(startupType, environmentName); object instance = null;
if (!configureMethod.MethodInfo.IsStatic || (servicesMethod != null && !servicesMethod.MethodInfo.IsStatic))
{
instance = ActivatorUtilities.GetServiceOrCreateInstance(hostingServiceProvider, startupType);
}
//构建一个执行ConfigService的委托
var configureServicesCallback = servicesMethod.Build(instance);
var configureContainerCallback = configureContainerMethod.Build(instance); Func<IServiceCollection, IServiceProvider> configureServices = services =>
{
// Call ConfigureServices, if that returned an IServiceProvider, we're done,执行ConfigService,如果执行ConfigService方法返回的是一个IServiceProvider,则就直接返回,不执行下面的替换ConfigContainer的方法
IServiceProvider applicationServiceProvider = configureServicesCallback.Invoke(services); if (applicationServiceProvider != null)
{
return applicationServiceProvider;
} // If there's a ConfigureContainer method
if (configureContainerMethod.MethodInfo != null)
{
// We have a ConfigureContainer method, get the IServiceProviderFactory<TContainerBuilder>
var serviceProviderFactoryType = typeof(IServiceProviderFactory<>).MakeGenericType(configureContainerMethod.GetContainerType());
var serviceProviderFactory = hostingServiceProvider.GetRequiredService(serviceProviderFactoryType);
// var builder = serviceProviderFactory.CreateBuilder(services);
var builder = serviceProviderFactoryType.GetMethod(nameof(DefaultServiceProviderFactory.CreateBuilder)).Invoke(serviceProviderFactory, new object[] { services });
configureContainerCallback.Invoke(builder);
// applicationServiceProvider = serviceProviderFactory.CreateServiceProvider(builder);
applicationServiceProvider = (IServiceProvider)serviceProviderFactoryType.GetMethod(nameof(DefaultServiceProviderFactory.CreateServiceProvider)).Invoke(serviceProviderFactory, new object[] { builder });
}
else
{
// Get the default factory
var serviceProviderFactory = hostingServiceProvider.GetRequiredService<IServiceProviderFactory<IServiceCollection>>(); // Don't bother calling CreateBuilder since it just returns the default service collection
applicationServiceProvider = serviceProviderFactory.CreateServiceProvider(services);
} return applicationServiceProvider ?? services.BuildServiceProvider();
}; return new StartupMethods(instance, configureMethod.Build(instance), configureServices);
}
ConventionBasedStartup这个类就是一个类似于StartUp的类,New得到时候,就是把StartupMethods传进去,里面的话,就是有两个方法Config指向StartUp的Config方法,ConfigService方法执行StartUp的ConfigService方法。
最后就是WebHostBuilder.Build()方法
public IWebHost Build()
{
if (_webHostBuilt)
{
throw new InvalidOperationException(Resources.WebHostBuilder_SingleInstance);
}
_webHostBuilt = true; // Warn about deprecated environment variables
if (Environment.GetEnvironmentVariable("Hosting:Environment") != null)
{
Console.WriteLine("The environment variable 'Hosting:Environment' is obsolete and has been replaced with 'ASPNETCORE_ENVIRONMENT'");
} if (Environment.GetEnvironmentVariable("ASPNET_ENV") != null)
{
Console.WriteLine("The environment variable 'ASPNET_ENV' is obsolete and has been replaced with 'ASPNETCORE_ENVIRONMENT'");
} if (Environment.GetEnvironmentVariable("ASPNETCORE_SERVER.URLS") != null)
{
Console.WriteLine("The environment variable 'ASPNETCORE_SERVER.URLS' is obsolete and has been replaced with 'ASPNETCORE_URLS'");
} var hostingServices = BuildCommonServices(out var hostingStartupErrors);
var applicationServices = hostingServices.Clone();
var hostingServiceProvider = hostingServices.BuildServiceProvider(); AddApplicationServices(applicationServices, hostingServiceProvider); var host = new WebHost(
applicationServices,
hostingServiceProvider,
_options,
_config,
hostingStartupErrors); host.Initialize(); return host;
}
里面的重点是在BuildCommonServices中,里面默认添加了一系列的Service。如下面的代码:
private IServiceCollection BuildCommonServices(out AggregateException hostingStartupErrors)
{
hostingStartupErrors = null; _options = new WebHostOptions(_config); if (!_options.PreventHostingStartup)
{
var exceptions = new List<Exception>(); // Execute the hosting startup assemblies
// HostingStartupAssemblies = $"{ApplicationName};{configuration[WebHostDefaults.HostingStartupAssembliesKey]}".Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries) ?? new string[0];这就是HostingStartupAssembly查找,当我们有类继承与IHostingStartup是,他必须得在build的config里面添加一个key为WebHostDefaults.HostingStartupAssembliesKey的程序集名称,多个以分号隔开。
foreach (var assemblyName in _options.HostingStartupAssemblies)
{
try
{
var assembly = Assembly.Load(new AssemblyName(assemblyName)); //程序集上必须打上HostingStartupAttribute标签
foreach (var attribute in assembly.GetCustomAttributes<HostingStartupAttribute>())
{
var hostingStartup = (IHostingStartup)Activator.CreateInstance(attribute.HostingStartupType);
//执行hostingStartup的config方法。
hostingStartup.Configure(this);
}
}
catch (Exception ex)
{
// Capture any errors that happen during startup
exceptions.Add(new InvalidOperationException($"Startup assembly {assemblyName} failed to execute. See the inner exception for more details.", ex));
}
} if (exceptions.Count > )
{
hostingStartupErrors = new AggregateException(exceptions);
}
} var contentRootPath = ResolveContentRootPath(_options.ContentRootPath, AppContext.BaseDirectory);
var applicationName = _options.ApplicationName; // Initialize the hosting environment
_hostingEnvironment.Initialize(applicationName, contentRootPath, _options);
_context.HostingEnvironment = _hostingEnvironment; var services = new ServiceCollection();
services.AddSingleton(_hostingEnvironment);
services.AddSingleton(_context); var builder = new ConfigurationBuilder()
.SetBasePath(_hostingEnvironment.ContentRootPath)
.AddInMemoryCollection(_config.AsEnumerable()); //执行之前添加的配置文件的委托
foreach (var configureAppConfiguration in _configureAppConfigurationBuilderDelegates)
{
configureAppConfiguration(_context, builder);
} 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>();
//之前再UseStartUp(string assemblyName)设置的startup类的程序集,在这之前也执行了 一段这个代码,在UseStartUp里面
//UseStartUp<T>(),这个重载的方法里面是没有加入这个程序集名称的,只是设置一下UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName)
if (!string.IsNullOrEmpty(_options.StartupAssembly))
{
try
{
var startupType = StartupLoader.FindStartupType(_options.StartupAssembly, _hostingEnvironment.EnvironmentName); if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo()))
{
services.AddSingleton(typeof(IStartup), startupType);
}
else
{
services.AddSingleton(typeof(IStartup), sp =>
{
var hostingEnvironment = sp.GetRequiredService<IHostingEnvironment>();
var methods = StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName);
return new ConventionBasedStartup(methods);
});
}
}
catch (Exception ex)
{
var capture = ExceptionDispatchInfo.Capture(ex);
services.AddSingleton<IStartup>(_ =>
{
capture.Throw();
return null;
});
}
}
//到这里就已经把说有的Service加到ServiceCollection
foreach (var configureServices in _configureServicesDelegates)
{
configureServices(_context, services);
} return services;
}
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里面都是同一个。
private void AddApplicationServices(IServiceCollection services, IServiceProvider hostingServiceProvider)
{
// We are forwarding services from hosting contrainer so hosting container
// can still manage their lifetime (disposal) shared instances with application services.
// NOTE: This code overrides original services lifetime. Instances would always be singleton in
// application container.
var listener = hostingServiceProvider.GetService<DiagnosticListener>();
services.Replace(ServiceDescriptor.Singleton(typeof(DiagnosticListener), listener));
services.Replace(ServiceDescriptor.Singleton(typeof(DiagnosticSource), listener));
}
最后是新建一个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的思想!
Asp.net Core 启动流程分析的更多相关文章
- 一张图理清ASP.NET Core启动流程
1. 引言 对于ASP.NET Core应用程序来说,我们要记住非常重要的一点是:其本质上是一个独立的控制台应用,它并不是必需在IIS内部托管且并不需要IIS来启动运行(而这正是ASP.NET Cor ...
- ASP.NET Core启动流程
1. 引言 对于ASP.NET Core应用程序来说,我们要记住非常重要的一点是:其本质上是一个独立的控制台应用,它并不是必需在IIS内部托管且并不需要IIS来启动运行(而这正是ASP.NET Cor ...
- Asp.net Core启动流程讲解(四)
Asp.net Core内 DI(DependencyInjection)贯穿了项目的始终,要学习Asp.net Core就无法越过DI. 下面讲解一下DI在Asp.Net Core内的流程 asp. ...
- Asp.net core 启动流程
- 一个由正则表达式引发的血案 vs2017使用rdlc实现批量打印 vs2017使用rdlc [asp.net core 源码分析] 01 - Session SignalR sql for xml path用法 MemCahe C# 操作Excel图形——绘制、读取、隐藏、删除图形 IOC,DIP,DI,IoC容器
1. 血案由来 近期我在为Lazada卖家中心做一个自助注册的项目,其中的shop name校验规则较为复杂,要求:1. 英文字母大小写2. 数字3. 越南文4. 一些特殊字符,如“&”,“- ...
- ASP.NET Core[源码分析篇] - WebHost
_configureServicesDelegates的承接 在[ASP.NET Core[源码分析篇] - Startup]这篇文章中,我们得知了目前为止(UseStartup),所有的动作都是在_ ...
- u-boot启动流程分析(2)_板级(board)部分
转自:http://www.wowotech.net/u-boot/boot_flow_2.html 目录: 1. 前言 2. Generic Board 3. _main 4. global dat ...
- u-boot启动流程分析(1)_平台相关部分
转自:http://www.wowotech.net/u-boot/boot_flow_1.html 1. 前言 本文将结合u-boot的“board—>machine—>arch—> ...
- GEF入门实例_总结_04_Eclipse插件启动流程分析
一.前言 本文承接上一节:GEF入门实例_总结_03_显示菜单和工具栏 注意到app目录下的6个类文件. 这6个文件对RCP应用程序而言非常重要,可能我们现在对这几个文件的理解还是云里雾里,这一节我们 ...
随机推荐
- C++拷贝构造函数(深拷贝,浅拷贝)
http://www.cnblogs.com/BlueTzar/articles/1223313.html C++拷贝构造函数(深拷贝,浅拷贝) 对于普通类型的对象来说,它们之间的复制是很简单的,例如 ...
- Codeforces Round #250 (Div. 2) D. The Child and Zoo 并查集
D. The Child and Zoo time limit per test 2 seconds memory limit per test 256 megabytes input standar ...
- matlab中的科学记数法变成小数形式
例如:假如rectx的形式在命令窗口显示: rectx = 1.0e+05 * 5.2294 5.2294 5.2294 5.2294 5.2294 那么,命令窗口输入vpa(rectx): ans ...
- 针对firefox ie6 ie7 ie8的css样式中的line-height属性
针对firefox ie6 ie7 ie8的css样式中的line-height属性 以前我们大部分都是用!important来hack,对于ie6和firefox测试可以正常显示,但是ie7以上对! ...
- Unity3D之Mesh(六)绘制扇形、扇面、环形
前言: 绘制了圆,就想到绘制与之相关的几何图形,以便更灵活的掌握Mesh动态创建模型的机制与方法. 一.分析: 首先,结合绘制圆的过程绘制环形: 圆形是由segments个等腰三角形组成的(上一篇中, ...
- QListWidget列表控件:当鼠标选中某行时,系统会自动设置选中的行的行号,用currentRow()返回回来,没有setCheck或setSelect类似函数
列表控件的设计思路: 只有QListWidgetItem自己能改变自己的状态(如checked,selected,颜色等)状态,QListWidget是无法改变其项的状态的. 列表控件是被动接受子项的 ...
- 【leetcode刷题笔记】Minimum Depth of Binary Tree
Given a binary tree, find its minimum depth. The minimum depth is the number of nodes along the shor ...
- freeMarker(十二)——模板语言补充知识
学习笔记,选自freeMarker中文文档,译自 Email: ddekany at users.sourceforge.net 1.特殊变量参考 特殊变量是由FreeMarker引擎自己定义的变量. ...
- 搭建 Http Dynamic Streaming 点播/直播服务器
1. HTTP Origin Module的处理数据流: a) 客户端发送媒体索引请求到Apache.例如: http://www.example.com/media/ ...
- NET Remoting 最简单示例
NET Remoting 最简单示例 2014-01-21 15:29 10492人阅读 评论(4) 收藏 举报 分类: .NET(6) 版权声明:本文为博主原创文章,未经博主允许不得转载. 学习 ...