|
回顾:
首先附上 ASP.NET Core GitHub 开源地址
https://github.com/dotnet/aspnetcore
https://github.com/dotnet/runtime
在新建一个 ASP.NET Core 3.1 的项目后,program 代码如下:
我们针对该入口代码结合 GitHub 源码,展开深入刨析。
Host 代码:https://github.com/dotnet/runtime/blob/79ae74f5ca5c8a6fe3a48935e85bd7374959c570/src/libraries/Microsoft.Extensions.Hosting/src/Host.cs
CreateDefaultBuilder 方法
这里实例化一个 HostBuilder 对象,HostBuilder 继承于 IHostBuilder,添加系统自定义的委托,例如:appsettings.json、appsettings.{env.EnvironmentName}.json 配置文件,最终会返回:IHostBuilder 接口。
HostBuilder 代码:https://github.com/dotnet/runtime/blob/79ae74f5ca5c8a6fe3a48935e85bd7374959c570/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs#L20
最终会调用 Build 方法。
public IHost Build()
{
if (_hostBuilt)
{
throw new InvalidOperationException(SR.BuildCalled);
}
_hostBuilt = true;
BuildHostConfiguration();
CreateHostingEnvironment();
CreateHostBuilderContext();
BuildAppConfiguration();
CreateServiceProvider();
return _appServices.GetRequiredService<IHost>();
} BuildHostConfiguration 方法
private IConfiguration _hostConfiguration;
IConfigurationBuilder configBuilder = new ConfigurationBuilder() 依次调用委托添加配置
CreateHostingEnvironment 方法
private HostingEnvironment _hostingEnvironment;
_hostingEnvironment = new HostingEnvironment()
{
ApplicationName = _hostConfiguration[HostDefaults.ApplicationKey],
EnvironmentName = _hostConfiguration[HostDefaults.EnvironmentKey] ?? Environments.Production,
ContentRootPath = ResolveContentRootPath(_hostConfiguration[HostDefaults.ContentRootKey], AppContext.BaseDirectory),
}; CreateHostBuilderContext 方法
private HostBuilderContext _hostBuilderContext;
_hostBuilderContext = new HostBuilderContext(Properties)
{
HostingEnvironment = _hostingEnvironment,
Configuration = _hostConfiguration
}; BuildAppConfiguration 方法
再次融合配置信息
IConfigurationBuilder configBuilder = new ConfigurationBuilder()
.SetBasePath(_hostingEnvironment.ContentRootPath)
.AddConfiguration(_hostConfiguration, shouldDisposeConfiguration: true);
_appConfiguration = configBuilder.Build();
_hostBuilderContext.Configuration = _appConfiguration; CreateServiceProvider 方法
var services = new ServiceCollection(); 注册服务,调用委托,添加用户自定义的服务。
GenericHostBuilderExtensions 扩展方法
https://github.com/dotnet/aspnetcore/blob/c925f99cddac0df90ed0bc4a07ecda6b054a0b02/src/DefaultBuilder/src/GenericHostBuilderExtensions.cs
ConfigureWebHost 扩展方法
将 GenericWebHostService 注册成后台服务:
builder.ConfigureServices((context, services) => services.AddHostedService<GenericWebHostService>()); GenericWebHostService 代码:https://github.com/dotnet/aspnetcore/blob/f1b0157a111bab8497c1e04241a18f1a60086d5a/src/Hosting/Hosting/src/GenericHost/GenericWebHostedService.cs
public async Task StartAsync(CancellationToken cancellationToken)
{
HostingEventSource.Log.HostStart();
var serverAddressesFeature = Server.Features.Get<IServerAddressesFeature>();
var addresses = serverAddressesFeature?.Addresses;
if (addresses != null && !addresses.IsReadOnly && addresses.Count == 0)
{
var urls = Configuration[WebHostDefaults.ServerUrlsKey];
if (!string.IsNullOrEmpty(urls))
{
serverAddressesFeature!.PreferHostingUrls = WebHostUtilities.ParseBool(Configuration, WebHostDefaults.PreferHostingUrlsKey);
foreach (var value in urls.Split(';', StringSplitOptions.RemoveEmptyEntries))
{
addresses.Add(value);
}
}
}
RequestDelegate? application = null;
try
{
var configure = Options.ConfigureApplication;
if (configure == null)
{
throw new InvalidOperationException($"No application configured. Please specify an application via IWebHostBuilder.UseStartup, IWebHostBuilder.Configure, or specifying the startup assembly via {nameof(WebHostDefaults.StartupAssemblyKey)} in the web host configuration.");
}
var builder = ApplicationBuilderFactory.CreateBuilder(Server.Features);
foreach (var filter in StartupFilters.Reverse())
{
configure = filter.Configure(configure);
}
configure(builder);
// Build the request pipeline
application = builder.Build();
}
catch (Exception ex)
{
Logger.ApplicationError(ex);
if (!Options.WebHostOptions.CaptureStartupErrors)
{
throw;
}
var showDetailedErrors = HostingEnvironment.IsDevelopment() || Options.WebHostOptions.DetailedErrors;
application = ErrorPageBuilder.BuildErrorPageApplication(HostingEnvironment.ContentRootFileProvider, Logger, showDetailedErrors, ex);
}
var httpApplication = new HostingApplication(application, Logger, DiagnosticListener, HttpContextFactory);
await Server.StartAsync(httpApplication, cancellationToken);
if (addresses != null)
{
foreach (var address in addresses)
{
LifetimeLogger.ListeningOnAddress(address);
}
}
if (Logger.IsEnabled(LogLevel.Debug))
{
foreach (var assembly in Options.WebHostOptions.GetFinalHostingStartupAssemblies())
{
Logger.StartupAssemblyLoaded(assembly);
}
}
if (Options.HostingStartupExceptions != null)
{
foreach (var exception in Options.HostingStartupExceptions.InnerExceptions)
{
Logger.HostingStartupAssemblyError(exception);
}
}
}
var webhostBuilder = new GenericWebHostBuilder(builder, webHostBuilderOptions); https://github.com/dotnet/aspnetcore/blob/c925f99cddac0df90ed0bc4a07ecda6b054a0b02/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs#L22
WebHostBuilderExtensions 扩展方法
提供 startupType 对象,供 IWebHostBuilder 调用。
this IWebHostBuilder hostBuilder
if (hostBuilder is ISupportsStartup supportsStartup)
{
return supportsStartup.UseStartup(startupType);
} https://github.com/dotnet/aspnetcore/blob/c925f99cddac0df90ed0bc4a07ecda6b054a0b02/src/Hosting/Hosting/src/WebHostBuilderExtensions.cs
GenericWebHostBuilder 私有方法 GenericWebHostBuilder
动态实例化我们的 Startup 对象:
instance ??= ActivatorUtilities.CreateInstance(new HostServiceProvider(webHostBuilderContext), startupType);
context.Properties[_startupKey] = instance; 寻找 ConfigureServices 方法
var configureServicesBuilder = StartupLoader.FindConfigureServicesDelegate(startupType, context.HostingEnvironment.EnvironmentName);
var configureServices = configureServicesBuilder.Build(instance);
internal static ConfigureServicesBuilder FindConfigureServicesDelegate([DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType, string environmentName)
{
var servicesMethod = FindMethod(startupType, "Configure{0}Services", environmentName, typeof(IServiceProvider), required: false)
?? FindMethod(startupType, "Configure{0}Services", environmentName, typeof(void), required: false);
return new ConfigureServicesBuilder(servicesMethod); 寻找 ConfigureContainer 方法
var configureContainerBuilder = StartupLoader.FindConfigureContainerDelegate(startupType, context.HostingEnvironment.EnvironmentName);
internal static ConfigureContainerBuilder FindConfigureContainerDelegate([DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType, string environmentName)
{
var configureMethod = FindMethod(startupType, "Configure{0}Container", environmentName, typeof(void), required: false);
return new ConfigureContainerBuilder(configureMethod);
} 寻找 Configure 方法
configureBuilder = StartupLoader.FindConfigureDelegate(startupType, context.HostingEnvironment.EnvironmentName);
internal static ConfigureBuilder FindConfigureDelegate([DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType, string environmentName)
{
var configureMethod = FindMethod(startupType, "Configure{0}", environmentName, typeof(void), required: true)!;
return new ConfigureBuilder(configureMethod);
} 由 ConfigureBuilder 的 Build 方法调用,源码如下:
private class ConfigureBuilder
{
public ConfigureBuilder(MethodInfo configure)
{
MethodInfo = configure;
}
public MethodInfo MethodInfo { get; }
public Action<IApplicationBuilder> Build(object instance)
{
return (applicationBuilder) => Invoke(instance, applicationBuilder);
}
private void Invoke(object instance, IApplicationBuilder builder)
{
var serviceProvider = builder.ApplicationServices;
var parameterInfos = MethodInfo.GetParameters();
var parameters = new object[parameterInfos.Length];
for (var index = 0; index < parameterInfos.Length; index++)
{
var parameterInfo = parameterInfos[index];
if (parameterInfo.ParameterType == typeof(IApplicationBuilder))
{
parameters[index] = builder;
}
else
{
try
{
parameters[index] = serviceProvider.GetRequiredService(parameterInfo.ParameterType);
}
catch (Exception ex)
{
throw new InvalidOperationException(
Resources.FormatMiddlewareFilter_ServiceResolutionFail(
parameterInfo.ParameterType.FullName,
parameterInfo.Name,
MethodInfo.Name,
MethodInfo.DeclaringType.FullName),
ex);
}
}
}
MethodInfo.Invoke(instance, parameters);
}
} Run 方法
HostingAbstractionsHostExtensions 扩展方法地址:
https://github.com/dotnet/runtime/blob/79ae74f5ca5c8a6fe3a48935e85bd7374959c570/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/HostingAbstractionsHostExtensions.cs
最终会调用:
/// <summary>
/// Runs an application and returns a Task that only completes when the token is triggered or shutdown is triggered.
/// </summary>
/// <param name="host">The <see cref="IHost"/> to run.</param>
/// <param name="token">The token to trigger shutdown.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public static async Task RunAsync(this IHost host, CancellationToken token = default)
{
try
{
await host.StartAsync(token).ConfigureAwait(false);
await host.WaitForShutdownAsync(token).ConfigureAwait(false);
}
finally
{
if (host is IAsyncDisposable asyncDisposable)
{
await asyncDisposable.DisposeAsync().ConfigureAwait(false);
}
else
{
host.Dispose();
}
}
} 之前在 Build 的时候,会注册 IHost 服务,代码如下:
services.AddSingleton<IHost>(_ =>
{
return new Internal.Host(_appServices,
_appServices.GetRequiredService<IHostApplicationLifetime>(),
_appServices.GetRequiredService<ILogger<Internal.Host>>(),
_appServices.GetRequiredService<IHostLifetime>(),
_appServices.GetRequiredService<IOptions<HostOptions>>());
}); StartAsync 方法
地址:https://github.com/dotnet/runtime/blob/79ae74f5ca5c8a6fe3a48935e85bd7374959c570/src/libraries/Microsoft.Extensions.Hosting/src/Internal/Host.cs#L39
public async Task StartAsync(CancellationToken cancellationToken = default)
{
_logger.Starting();
using var combinedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _applicationLifetime.ApplicationStopping);
CancellationToken combinedCancellationToken = combinedCancellationTokenSource.Token;
await _hostLifetime.WaitForStartAsync(combinedCancellationToken).ConfigureAwait(false);
combinedCancellationToken.ThrowIfCancellationRequested();
_hostedServices = Services.GetService<IEnumerable<IHostedService>>();
foreach (IHostedService hostedService in _hostedServices)
{
// Fire IHostedService.Start
await hostedService.StartAsync(combinedCancellationToken).ConfigureAwait(false);
if (hostedService is BackgroundService backgroundService)
{
_ = HandleBackgroundException(backgroundService);
}
}
// Fire IHostApplicationLifetime.Started
_applicationLifetime.NotifyStarted();
_logger.Started();
} (完)
|
上一篇:迟到的新人报道下一篇:.NET Core 在 Linux 错误 Failure processing application bundle
|