创建项目后我们从Program类中可以看到以下代码:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
private static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
查看以上代码可以发现 Main 方法中代码很简单 ,清晰可见
CreateHostBuilder(args) :方法创建了一个IHostBuilder 抽象对象,创建过程包含CreateDefaultBuilder(args):开启创建一个默认的通用宿主机Host建造者,再通过ConfigureWebHostDefaults()方法配置开启默认的Kestrel为默认的Web服务器并对其进行默认配置,并集成对iis的集成 Build() :负责创建IHost,看过源代码的同学可以发现Build的过程 会配置各种东西,本身通过管道模式进行了一系列的默认或者自定义的配置以及服务注册的构建(下面会详细讲解) Run() :启动IHost
所以,ASP.NET Core应用的启动本质上是启动作为宿主的Host对象。
其主要涉及到两个关键对象IHostBuilder和IHost,它们的内部实现是ASP.NET Core应用的核心所在。下面我们就结合源码并梳理调用堆栈来一探究竟!
源代码详细图如下:
从上图中我们可以看出CreateDefaultBuilder()方法主要干了五件大事:
- UseContentRoot:指定Web host使用的content root(内容根目录),比如Views。默认为当前应用程序根目录。
- ConfigureHostConfiguration :启动时宿主机需要的环境变量等相关,支持命令行
- ConfigureAppConfiguration:设置当前应用程序配置。主要是读取 appsettinggs.json 配置文件、开发环境中配置的UserSecrets、添加环境变量和命令行参数 。
- ConfigureLogging:读取配置文件中的Logging节点,配置日志系统。
- UseDefaultServiceProvider:设置默认的依赖注入容器。
从图中可以看出CreateDefaultBuilder 后调用了ConfigureWebHostDefaults 方法,该方法默认主要做了以下几个事情
- UseStaticWebAssets:静态文件环境的配置启用
- UseKestrel:开启Kestrel为默认的web 服务器.
- ConfigureServices:服务中间件的注册,包含路由的中间件的注册
- UseIIS:对iis 集成的支持
- UseStartup:程序Startup 启动,该启动类中可以注册中间件、扩展第三方中间件,以及相关应用程序配置的处理等等操作
现在所有的配置都已经配置创建好了,接下来我们来看看Build 方法主要做了哪些不为人知的事情,先来看下源代码
/// <summary>
/// Run the given actions to initialize the host. This can only be called once.
/// </summary>
/// <returns>An initialized <see cref="IHost"/></returns>
public IHost Build()
{
if (_hostBuilt)
{
throw new InvalidOperationException(SR.BuildCalled);
}
_hostBuilt = true;
// REVIEW: If we want to raise more events outside of these calls then we will need to
// stash this in a field.
using var diagnosticListener = new DiagnosticListener("Microsoft.Extensions.Hosting");
const string hostBuildingEventName = "HostBuilding";
const string hostBuiltEventName = "HostBuilt";
if (diagnosticListener.IsEnabled() && diagnosticListener.IsEnabled(hostBuildingEventName))
{
Write(diagnosticListener, hostBuildingEventName, this);
}
BuildHostConfiguration();
CreateHostingEnvironment();
CreateHostBuilderContext();
BuildAppConfiguration();
CreateServiceProvider();
var host = _appServices.GetRequiredService<IHost>();
if (diagnosticListener.IsEnabled() && diagnosticListener.IsEnabled(hostBuiltEventName))
{
Write(diagnosticListener, hostBuiltEventName, host);
}
return host;
}
从代码中可以发现有一个_hostBuilt 的变量,细心的同学可以发现该变量主要是用于控制是否build 过,所以这里可以大胆猜测只能build 一次该Host。
现在看下源代码解析图:
经过查看源代码得到的执行结构如上,因此我把代码改造成如下结构。
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)//开启一个默认的通用主机Host建造者
.ConfigureAppConfiguration(config => {
//注册应用程序内所使用的配置文件,比如数据库链接等等
Console.WriteLine("ConfigureAppConfiguration");
})
.ConfigureServices(service =>
{
//注册服务中间件等操作
Console.WriteLine("ConfigureServices");
})
.ConfigureHostConfiguration(builder => {
//启动时需要的组件配置等,比如监听的端口 url地址等
Console.WriteLine("ConfigureHostCOnfiguration");
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
通过执行发现执行代码顺序正如我们源代码的执行顺序一致,执行顺序如下图: