本系列将分析ASP.NET Core运行原理

本节将分析WebHost.CreateDefaultBuilder(args).UseStartup<Startup>().Build();代码。

源代码参考.NET Core 2.0.0

问题概要

  1. Hosting中有哪2个ServiceProvider,各自如何创建,以及有哪些ServiceCollection。
  2. 什么时候执行Startup的ConfigureServices 和 Configure方法
  3. 什么时候注册和使用的Server
  4. 手工模拟一个DefaultWebHost环境

WebHost.CreateDefaultBuilder(args)

该方法为WebHost类的静态方法,内部创建1个WebHostBuilder。

  1. 参数args将作为配置项
  2. 添加了Kestrel、Configuration、Logging、IISIntegration中间件,同时配置ContentRoot和DefaultServiceProvider
public static IWebHostBuilder CreateDefaultBuilder(string[] args)
{
var builder = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, 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;
}

UseKestrel

UseKestrel()方法中,注册了3个服务到List<Action<WebHostBuilderContext, IServiceCollection>>字段上。(以供后续注册)

public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder)
{
return hostBuilder.ConfigureServices((Action<IServiceCollection>) (services =>
{
services.AddSingleton<ITransportFactory, LibuvTransportFactory>();
services.AddTransient<IConfigureOptions<KestrelServerOptions>, KestrelServerOptionsSetup>();
services.AddSingleton<IServer, KestrelServer>();
}));
} public IWebHostBuilder ConfigureServices(Action<WebHostBuilderContext, IServiceCollection> configureServices)
{
this._configureServicesDelegates.Add((_, services) => configureServices(services));
return (IWebHostBuilder) this;
}

UseContentRoot

UseContentRoot方法则是添加到IConfiguration字段上,这个字段在构造函数初始化

this._config = (IConfiguration) new ConfigurationBuilder().AddEnvironmentVariables("ASPNETCORE_").Build();

public static IWebHostBuilder UseContentRoot(this IWebHostBuilder hostBuilder, string contentRoot)
{
if (contentRoot == null)
throw new ArgumentNullException(nameof (contentRoot));
return hostBuilder.UseSetting(WebHostDefaults.ContentRootKey, contentRoot);
} public IWebHostBuilder UseSetting(string key, string value)
{
this._config[key] = value;
return (IWebHostBuilder) this;
}

ConfigureAppConfiguration

ConfigureAppConfiguration方法是添加到List<Action<WebHostBuilderContext, IConfigurationBuilder>>字段上

在外部添加了

AddJsonFile("appsettings.json")AddJsonFile(string.Format("appsettings.{0}.json", (object) hostingEnvironment.EnvironmentName))

AddEnvironmentVariables()

AddCommandLine(args)

public IWebHostBuilder ConfigureAppConfiguration(Action<WebHostBuilderContext, IConfigurationBuilder> configureDelegate)
{
this._configureAppConfigurationBuilderDelegates.Add(configureDelegate);
return (IWebHostBuilder) this;
}

ConfigureLogging

ConfigureLogging注册Log到ServiceCollection上

在外部添加了3个ILoggerProvider

logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));

logging.AddConsole();

logging.AddDebug();

public static IWebHostBuilder ConfigureLogging(this IWebHostBuilder hostBuilder, Action<WebHostBuilderContext, ILoggingBuilder> configureLogging)
{
return hostBuilder.ConfigureServices((context, services) => services.AddLogging(builder => configureLogging(context, builder));
} public IWebHostBuilder ConfigureServices(Action<WebHostBuilderContext, IServiceCollection> configureServices)
{
this._configureServicesDelegates.Add(configureServices);
return (IWebHostBuilder) this;
}

UseDefaultServiceProvider

UseDefaultServiceProvider配置和替换服务

var options = new ServiceProviderOptions { ValidateScopes = context.HostingEnvironment.IsDevelopment()};
services.Replace(ServiceDescriptor.Singleton<IServiceProviderFactory<IServiceCollection>>((IServiceProviderFactory<IServiceCollection>) new DefaultServiceProviderFactory(options)));

UseStartup

UseStartup相当于注册了一个IStartup服务。

public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType)
{
string name = startupType.GetTypeInfo().Assembly.GetName().Name;
return hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, name).ConfigureServices((Action<IServiceCollection>) (services =>
{
if (typeof (IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo()))
ServiceCollectionServiceExtensions.AddSingleton(services, typeof (IStartup), startupType);
else
ServiceCollectionServiceExtensions.AddSingleton(services, typeof (IStartup), (Func<IServiceProvider, object>) (sp =>
{
IHostingEnvironment requiredService = sp.GetRequiredService<IHostingEnvironment>();
return (object) new ConventionBasedStartup(StartupLoader.LoadMethods(sp, startupType, requiredService.EnvironmentName));
}));
}));
}

根据Startup是否继承IStartup,来决定注册的方式。未继承的时候,会使用ConventionBasedStartup来封装自定义的Startup。

Build

Build方法是WebHostBuilder最终的目的,将构造一个WebHost返回。

同时初始化WebHost对象,WebHostBuilder.Build代码:

public IWebHost Build()
{
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;
}

在Build方法中,BuildCommonServices最为重要,将构造第一个ServiceCollection。这里我们称为hostingServices

将包含hostEnv、Config、ApplicationBuilder、Logging、StartupFilter、Startup、Server。参考BuildCommonServices

private IServiceCollection BuildCommonServices(out AggregateException hostingStartupErrors)
{
if (!this._options.PreventHostingStartup)
{
foreach (string hostingStartupAssembly in (IEnumerable<string>) this._options.HostingStartupAssemblies)
{
foreach (HostingStartupAttribute customAttribute in Assembly.Load(new AssemblyName(hostingStartupAssembly)).GetCustomAttributes<HostingStartupAttribute>())
((IHostingStartup) Activator.CreateInstance(customAttribute.HostingStartupType)).Configure((IWebHostBuilder) this);
}
}
ServiceCollection services = new ServiceCollection();
// hostEnv
_hostingEnvironment.Initialize(this._options.ApplicationName, this.ResolveContentRootPath(this._options.ContentRootPath, AppContext.BaseDirectory), this._options);
services.AddSingleton<IHostingEnvironment>(this._hostingEnvironment);
// config
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder().SetBasePath(this._hostingEnvironment.ContentRootPath).AddInMemoryCollection(this._config.AsEnumerable());
foreach (Action<WebHostBuilderContext, IConfigurationBuilder> configurationBuilderDelegate in this._configureAppConfigurationBuilderDelegates)
configurationBuilderDelegate(this._context, configurationBuilder);
IConfigurationRoot configurationRoot = configurationBuilder.Build();
services.AddSingleton<IConfiguration>((IConfiguration) configurationRoot);
services.AddOptions();
// application
services.AddTransient<IApplicationBuilderFactory, ApplicationBuilderFactory>();
services.AddTransient<IHttpContextFactory, HttpContextFactory>();
services.AddScoped<IMiddlewareFactory, MiddlewareFactory>();
// log
services.AddLogging();
services.AddTransient<IStartupFilter, AutoRequestServicesStartupFilter>();
services.AddTransient<IServiceProviderFactory<IServiceCollection>, DefaultServiceProviderFactory>();
// 配置的StartupType
if (!string.IsNullOrEmpty(this._options.StartupAssembly))
{
Type startupType = StartupLoader.FindStartupType(this._options.StartupAssembly, this._hostingEnvironment.EnvironmentName);
if (typeof (IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo()))
ServiceCollectionServiceExtensions.AddSingleton(services, typeof (IStartup), startupType);
else
ServiceCollectionServiceExtensions.AddSingleton(services, typeof (IStartup), new ConventionBasedStartup(StartupLoader.LoadMethods(startupType)));
}
// UseStartup、UseKestrel、ConfigureLogging
foreach (Action<WebHostBuilderContext, IServiceCollection> servicesDelegate in this._configureServicesDelegates)
servicesDelegate(this._context, (IServiceCollection) services);
return (IServiceCollection) services;
}

hostingServices 创建完成后,会马上拷贝一份applicationServices提供给WebHost使用,同时创建第一个ServiceProvider hostingServiceProvider

host.Initialize()该方法则是初始化WebHost

  1. 生成第二个ServiceProvider _applicationServices(微软的内部字段命名,感觉不太规范);
  2. 初始化Server,读取绑定的地址。
  3. 创建Application管道,生成RequestDelegate
public void Initialize()
{
_startup = _hostingServiceProvider.GetService<IStartup>();
_applicationServices = _startup.ConfigureServices(_applicationServiceCollection);
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;
foreach (var filter in startupFilters.Reverse())
{
configure = filter.Configure(configure);
}
configure(builder); this._application = builder.Build(); // RequestDelegate
}

WebHost.Run

创建完 WebHost 之后,便调用它的 Run 方法,而 Run 方法会去调用 WebHost 的 StartAsync 方法

  1. 调用Server.Start,将Initialize方法创建的Application管道传入以供处理消息
  2. 执行HostedServiceExecutor.StartAsync方法
public static void Run(this IWebHost host)
{
host.RunAsync(cts.Token, "Application started. Press Ctrl+C to shut down.").GetAwaiter().GetResult();
} private static async Task RunAsync(this IWebHost host, CancellationToken token, string shutdownMessage)
{
await host.StartAsync(token);
var hostingEnvironment = host.Services.GetService<IHostingEnvironment>();
Console.WriteLine($"Hosting environment: {hostingEnvironment.EnvironmentName}");
Console.WriteLine($"Content root path: {hostingEnvironment.ContentRootPath}");
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);
} public virtual async Task StartAsync(CancellationToken cancellationToken = default (CancellationToken))
{
Initialize();
_applicationLifetime = _applicationServices.GetRequiredService<IApplicationLifetime>() as ApplicationLifetime;
_hostedServiceExecutor = _applicationServices.GetRequiredService<HostedServiceExecutor>();
var httpContextFactory = _applicationServices.GetRequiredService<IHttpContextFactory>();
var hostingApp = new HostingApplication(_application, _logger, diagnosticSource, httpContextFactory);
await Server.StartAsync(hostingApp, cancellationToken).ConfigureAwait(false); _applicationLifetime?.NotifyStarted();
await _hostedServiceExecutor.StartAsync(cancellationToken).ConfigureAwait(false);
}

问题解答

  1. hostingServices(WebHostBuilder) 和 _applicationServices(WebHost)。区别是_applicationServices中比hostingServices多处Startup方法注册的服务。
  2. 都是WebHost中执行的。ConfigureServices在_applicationServices生成前,Configure在_applicationServices生成后。
  3. 最初的注册在WebHostBuilder的UseKestrel,使用在WebHost的Run方法

手工模拟一个DefaultWebHost环境

new WebHostBuilder()
.UseKestrel() // 使用Kestrel服务器
.UseContentRoot(Directory.GetCurrentDirectory()) // 配置根目录 会在ConfigurationBuilder、 IHostingEnvironment(后续其他中间件) 和 WebHostOptions(WebHost)用到。
.ConfigureAppConfiguration((context, builder) => builder.AddJsonFile("appsetting.json", true, true)) // 添加配置源
.ConfigureLogging(builder => builder.AddConsole()) // 添加日志适配器
.UseStartup<Startup>() // 选择Startup
.Build().Run(); // 生成WebHost 并 启动

本文链接:http://neverc.cnblogs.com/p/7988226.html

【ASP.NET Core】运行原理(1):创建WebHost的更多相关文章

  1. ASP.NET Core 运行原理解剖[1]:Hosting

    ASP.NET Core 是新一代的 ASP.NET,第一次出现时代号为 ASP.NET vNext,后来命名为ASP.NET 5,随着它的完善与成熟,最终命名为 ASP.NET Core,表明它不是 ...

  2. ASP.NET Core 运行原理解剖[2]:Hosting补充之配置介绍

    在上一章中,我们介绍了 ASP.NET Core 的启动过程,主要是对 WebHost 源码的探索.而本文则是对上文的一个补充,更加偏向于实战,详细的介绍一下我们在实际开发中需要对 Hosting 做 ...

  3. ASP.NET Core 运行原理解剖[4]:进入HttpContext的世界

    HttpContext是ASP.NET中的核心对象,每一个请求都会创建一个对应的HttpContext对象,我们的应用程序便是通过HttpContext对象来获取请求信息,最终生成响应,写回到Http ...

  4. ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件)

    ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件) Startup Class 1.Startup Constructor(构造函数) 2.Configure ...

  5. ASP.NET Core 运行原理剖析1:初始化WebApp模版并运行

    ASP.NET Core 运行原理剖析1:初始化WebApp模版并运行 核心框架 ASP.NET Core APP 创建与运行 总结 之前两篇文章简析.NET Core 以及与 .NET Framew ...

  6. ASP.NET Core 运行原理解剖[3]:Middleware-请求管道的构成

    在 ASP.NET 中,我们知道,它有一个面向切面的请求管道,有19个主要的事件构成,能够让我们进行灵活的扩展.通常是在 web.config 中通过注册 HttpModule 来实现对请求管道事件监 ...

  7. ASP.NET Core 运行原理解剖[5]:Authentication

    在现代应用程序中,认证已不再是简单的将用户凭证保存在浏览器中,而要适应多种场景,如App,WebAPI,第三方登录等等.在 ASP.NET 4.x 时代的Windows认证和Forms认证已无法满足现 ...

  8. ASP.NET Core 运行原理剖析

    1. ASP.NET Core 运行原理剖析 1.1. 概述 1.2. 文件配置 1.2.1. Starup文件配置 Configure ConfigureServices 1.2.2. appset ...

  9. ASP.NET Core 运行原理剖析 (转载)

    1.1. 概述 在ASP.NET Core之前,ASP.NET Framework应用程序由IIS加载.Web应用程序的入口点由InetMgr.exe创建并调用托管.以初始化过程中触发HttpAppl ...

随机推荐

  1. Django学习---抽屉热搜榜分析【all】

    Python实例---抽屉热搜榜前端代码分析 Python实例---抽屉后台框架分析 Python学习---抽屉框架分析[点赞功能分析] Python学习---抽屉框架分析[数据库设计分析]18031 ...

  2. Python运算符之三元运算符

    三元运算符:也称之为条件表达式 [条件为真的结果] if 条件 else [条件为假的结果] 如: ium01 = 100 if100 > 200 else200 print(num01) #三 ...

  3. Spring+Hibernate实现动态SessionFactory切换

    场景: 1)系统有多个数据库 2)且数据库类型也不尽相同 3)现在应用根据某些条件路由到具体的数据库 4)且在spring+hibernate框架下,支持依赖注入 已有实现,spring动态数据源,但 ...

  4. hihocoder Round #c1(hihoCoder太阁最新面经算法竞赛1 )

    Test链接:https://cn.vjudge.net/contest/231849 选自hihoCoder太阁最新面经算法竞赛1 更多Test:传送门 A:区间求差 给一组区间集合A和区间集合B, ...

  5. rabbitmq的万能安装和外网访问(NC版)

    先去这个http://www.rabbitmq.com/releases/下载erlang环境和rpm(erlang的尽量高点,rabbitmq版本差不多就可以了,) erlang-19.0.4-1. ...

  6. 【洛谷】【堆】P1801 黑匣子_NOI导刊2010提高(06)

    [题目描述:] Black Box是一种原始的数据库.它可以储存一个整数数组,还有一个特别的变量i.最开始的时候Black Box是空的.而i等于0.这个Black Box要处理一串命令. 命令只有两 ...

  7. Docker实战(八)之Web服务与应用

    1.Apache 官方提供了名为httpd的Apache镜像,可以作为基础web服务镜像 Dockerfile(安装apache2) FROM httpd:2.4 COPY ./public-html ...

  8. pom xml testng

    <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId ...

  9. gulp合并压缩

    1.文件合并压缩 var concat = require(‘gulp-concat’); //引用 var uglify = require(‘gulp-uglify’);  接下来,只要conca ...

  10. POJ 2251 Dungeon Master(多层地图找最短路 经典bfs,6个方向)

    Dungeon Master Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 48380   Accepted: 18252 ...