研究原因:学习 .Net Core 两年有余,实际项目也使用了一年半,自己的技术已经到了瓶颈,需要有一个突破,我觉得首先研究架构师的设计思想,其次分析一下.Net Core的源码,这将会是一个很好的学习方式。

源码版本: .Net Core 3.1.14   ,有aspnetcore 和 extensions 两部分

下载地址:https://github.com/dotnet/aspnetcore  https://github.com/dotnet/extensions

参考资料:https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/startup?view=aspnetcore-3.1            微软官方文档

https://www.cnblogs.com/edison0621/p/11025310.html   .NET Core 3.0之深入源码理解Host(一)  艾心

首先从 Program.cs 代码讲起,可以看到,以前使用 .Net Core 2.0 的时候 创建的是WebHostBuilder对象,而现在变为了HostBuilder

public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
} public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});

Main 方法是项目的入口,但是实际上执行了三个函数,调用下面的CreateHostBuilder方法创建IHostBuilder对象,build一个实例,再run,运行。

上半部分Main()

主要就是 build() 和 run() 方法,这个很简单,我们先把后面解释一下。

下半部分CreateHostBuilder()

本篇介绍Program最复杂的就是这个创建IHostBuilder对象的过程,现在来逐一解析。

其实这个CreateHostBuilder 就三步骤

1.初始化一个IHostBuilder

2.把IHostBuidler转化为IWebHostBuilder ( 2.0 是直接创建,这里绕了一个弯 )

3.给IWebHostBuilder 配置一些信息 ( 例如后面可以配置 端口号,日志等)

下半第一步,初始化一个 IHostBuilder 对象

Host.CreateDefaultBuilder(args)  约等于 IHostBuilder builder = Host.CreateDefaultBuilder(args);

点进去这个方法查看源头,指向CreateDefaultBuilder(string[] args)方法。

在 ..\extensions-3.1.14\src\Hosting\Hosting\src\Host.cs 中找到这个方法的源码如下

        public static IHostBuilder CreateDefaultBuilder(string[] args)
{
var builder = new HostBuilder(); builder.UseContentRoot(Directory.GetCurrentDirectory());
builder.ConfigureHostConfiguration(config =>
{
config.AddEnvironmentVariables(prefix: "DOTNET_");
if (args != null)
{
config.AddCommandLine(args);
}
}); builder.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() && !string.IsNullOrEmpty(env.ApplicationName))
{
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) =>
{
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); // IMPORTANT: This needs to be added *before* configuration is loaded, this lets
// the defaults be overridden by the configuration.
if (isWindows)
{
// Default the EventLogLoggerProvider to warning or above
logging.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Warning);
} logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddDebug();
logging.AddEventSourceLogger(); if (isWindows)
{
// Add the EventLogLoggerProvider on windows machines
logging.AddEventLog();
}
})
.UseDefaultServiceProvider((context, options) =>
{
var isDevelopment = context.HostingEnvironment.IsDevelopment();
options.ValidateScopes = isDevelopment;
options.ValidateOnBuild = isDevelopment;
}); return builder;
}

好,现在从此方法第一句 var builder = new HostBuilder(); 开始逐一分解

指定Host要使用的根目录

 builder.UseContentRoot(Directory.GetCurrentDirectory());

配置初始化环境变量,如appsettings.json等。

builder.ConfigureHostConfiguration(config => ......

builder.ConfigureAppConfiguration((hostingContext, config) => ......

配置.Net Core 默认的Log

.ConfigureLogging((hostingContext, logging) => ......

配置开发环境模式下启用作用域验证

.UseDefaultServiceProvider((context, options) =>
{
var isDevelopment = context.HostingEnvironment.IsDevelopment();
options.ValidateScopes = isDevelopment;
options.ValidateOnBuild = isDevelopment;
});

下半第二步:把IHostBuidler对象转化为IWebHostBuilder 

这一步骤是通过Program.cs/CreateHostBuilder中的 .ConfigureWebHostDefaults()方法,点进去看看

这个方法如果你按照上面写的目录去找,是找不到的。查找了很久,才发现这个方法其实是在

.....\aspnetcore-3.1.14\src\DefaultBuilder\src 目录下,其实是调用在这里,很有欺骗性T.T,这里推荐一个搜文件的工具:searcheverything。

打开这个方法,源码如下,非常简单。

    public static class GenericHostBuilderExtensions
{
public static IHostBuilder ConfigureWebHostDefaults(this IHostBuilder builder, Action<IWebHostBuilder> configure)
{
return builder.ConfigureWebHost(webHostBuilder =>
{
WebHost.ConfigureWebDefaults(webHostBuilder); configure(webHostBuilder);
});
}
}

看到这里豁然开朗,知道了前面的意思了,看起来似乎很难理解,其实上面的方法可以理解为

var webHostBuilder = new GenericWebHostBuilder(builder);
configure(webHostBuilder);

下半第三步:给IWebHostBuilder 配置一些信息

把 hostBuilder 转化为 webHostBudiler对像,然后调用参数传入的委托 configure(webHostBuilder) , 这个configure实际就是Program.cs里面的

webBuilder.UseStartup<Startup>();

当然,你还可以使用委托配置log,配置端口,自定义各种配置,然后都到源码里这个方法装配

上半部分Main()

好,现在下半部分CreateHostBuilder()方法已经完全执行完了,回到上半部分的 Main()方法。

build方法在 ......\extensions-3.1.14\src\Hosting\Hosting\src\HostBuilder 中

        public IHost Build()
{
if (_hostBuilt)
{
throw new InvalidOperationException("Build can only be called once.");
}
_hostBuilt = true; BuildHostConfiguration();
CreateHostingEnvironment();
CreateHostBuilderContext();
BuildAppConfiguration();
CreateServiceProvider(); return _appServices.GetRequiredService<IHost>();
}

build() 方法只会执行一次,创建各种配置过的对象。

run 方法在 ......\extensions-3.1.14\src\Hosting\Abstractions\src\HostingAbstractionsHostExtensions 中

        public static void Run(this IHost host)
{
host.RunAsync().GetAwaiter().GetResult();
}

Run方法运行应用程序,阻止调用线程,使方法一直运行,提供服务,直到主机关闭

hist.RunAsync() 中的 RunAsync()方法如下,host 的创建和销毁,可以通过设置CancellationToken 的值,使程序自动关闭

        public static async Task RunAsync(this IHost host, CancellationToken token = default)
{
try
{
await host.StartAsync(token); await host.WaitForShutdownAsync(token);
}
finally
{
if (host is IAsyncDisposable asyncDisposable)
{
await asyncDisposable.DisposeAsync();
}
else
{
host.Dispose();
} }
}

总结:

看起来非常复杂,但是经过细细分析,发现其实很简单,就是创建builder ,通过builder生成host ,最后执行host。

其他思考:

为什么.Net Core 3.1 与 2.X 比 生成 host 的过程 改的更加复杂???

结合巨硬后来的各种操作比如.Net5 .Net6,我明白了,虽然需要的是IWebHostBuilder,但我还是要抽象一个IHostBuilder

这样可以更灵活的配置,使.Net Core 不再只是为了 web服务,未来应该巨硬是想统一化生态园,WPF、Xamarin、Unity 等技术会渐渐整合起来.

期待今年11月份的 .Net 6 LTS版本 ,C#设计的这么牛皮的语言不能让他没落,多输出内容,让.Net未来的生态变的更好吧。

【.Net Core】.Net Core 源码分析与深入理解 - 入口 Program.cs (一)的更多相关文章

  1. 学习JUC源码(3)——Condition等待队列(源码分析结合图文理解)

    前言 在Java多线程中的wait/notify通信模式结尾就已经介绍过,Java线程之间有两种种等待/通知模式,在那篇博文中是利用Object监视器的方法(wait(),notify().notif ...

  2. Spring Boot 2.x 启动全过程源码分析(上)入口类剖析

    Spring Boot 的应用教程我们已经分享过很多了,今天来通过源码来分析下它的启动过程,探究下 Spring Boot 为什么这么简便的奥秘. 本篇基于 Spring Boot 2.0.3 版本进 ...

  3. Java ArrayList源码分析(有助于理解数据结构)

    arraylist源码分析 1.数组介绍 数组是数据结构中很基本的结构,很多编程语言都内置数组,类似于数据结构中的线性表 在java中当创建数组时会在内存中划分出一块连续的内存,然后当有数据进入的时候 ...

  4. 学习JUC源码(1)——AQS同步队列(源码分析结合图文理解)

    前言 最近结合书籍<Java并发编程艺术>一直在看AQS的源码,发现AQS核心就是:利用内置的FIFO双向队列结构来实现线程排队获取int变量的同步状态,以此奠定了很多并发包中大部分实现基 ...

  5. ci之 core下CodeIgniter源码分析(1)

    ci 执行流程 index.php 文件 加载codeigniter文件 codeigniter部分里面加载的: 加载配置文件constants 加载全局公共函数core/Common.php 文件 ...

  6. quartz源码分析之深刻理解job,sheduler,calendar,trigger及listener之间的关系

    org.quartz包 包org.quartz是Quartz的主包,包含了客户端接口. 其中接口有: Calendar接口: 定义了一个关联Trigger可能(或者不可能)触发的时间空间.它没有定义触 ...

  7. Django源码分析之程序执行入口分析

    一般我们开启一个django项目,最简单的方法是进入project 目录,这时目录结构是这样的 然后我们执行python manage.py runserver,程序就开始执行了. 那django是如 ...

  8. nova创建虚拟机源码分析系列之六 api入口create方法

    openstack 版本:Newton 注:博文图片采用了很多大牛博客图片,仅作为总结学习,非商用.该图全面的说明了nova创建虚机的过程,从逻辑的角度清晰的描述了前端请求创建虚拟机之后发生的一系列反 ...

  9. Dubbo 源码分析 - 服务引用

    1. 简介 在上一篇文章中,我详细的分析了服务导出的原理.本篇文章我们趁热打铁,继续分析服务引用的原理.在 Dubbo 中,我们可以通过两种方式引用远程服务.第一种是使用服务直联的方式引用服务,第二种 ...

  10. 涨姿势:Spring Boot 2.x 启动全过程源码分析

    目录 SpringApplication 实例 run 方法运行过程 总结 上篇<Spring Boot 2.x 启动全过程源码分析(一)入口类剖析>我们分析了 Spring Boot 入 ...

随机推荐

  1. [转帖]docker使用buildx构建多平台(x86,arm64)构架镜像

    https://blog.csdn.net/atzqtzq/article/details/128583331 配置文件激活buildx docker目前使用版本为Server Version: 20 ...

  2. Linux应用程序下网络栈参数的简单整理

    somaxconn 该参数应该是决定一个服务能够同时处理多少个网络请求的核心参数. 一个程序能够支持多少个访问参数,是有两部分来决定, 第一部分是somaxconn ,第二部分是应用服务器启动时传递过 ...

  3. Spring Boot集成Actuator

    一.Spring-Boot-Actuator简介 官网:https://docs.spring.io/spring-boot/docs/2.3.4.BUILD-SNAPSHOT/reference/h ...

  4. vue动画 <transition-group> 使用半场动画

    <style> /* 给动画添加一组过度效果 */ .v-enter, .v-leave-to { opacity: 0.8; /* 从右向左进入 */ transform: transl ...

  5. 基于Basic auth 的一个C# 示例

    最近在做公司的一个项目时,客户需要我们定时获取他们矩阵系统的数据.在与客户进行对接时,提到他们的接口使用的目前不常用的BASIC 认证.天呢,它好不安全,容易被不法人监听,咋还在使用呀.但是没办法呀, ...

  6. TC插件管理器及在TC中新建文件

    新建文件 Shift+F4可以新建文件,但是会调用内置的编辑器来打开. 在设置中通过修改F4快捷键对应的程序来修改. PS.F4编辑没找到根据扩展名来调用指定程序打开,可以F3中进行补充. NewFi ...

  7. GO中的GC

    go中的垃圾回收 前言 垃圾回收 go中的垃圾回收方式 三色标记法 根对象 STW 屏障技术 插入屏障 删除屏障 混合写屏障 GO中GC的流程 GC的触发时机 如果内存分配速度超过了标记清除的速度怎么 ...

  8. Midjourney|文心一格prompt教程[技巧篇]:生成多样性、增加艺术风格、图片二次修改、渐进优化、权重、灯光设置等17个技巧等你来学

    Midjourney|文心一格prompt教程[技巧篇]:生成多样性.增加艺术风格.图片二次修改.渐进优化.权重.灯光设置等17个技巧等你来学 1.技巧一:临摹 我认为学习图片类的 prompt,跟学 ...

  9. 一些提供办公效率的软件(clover、f.lux、幕布),老赞了!

    1.clover 链接:http://cn.ejie.me/ Clover是由异次元的读者ejie团队开发的一款电脑窗口标签化工具.Clover是电脑中资源管理器的一个扩展程序,可以为其增加多标签页的 ...

  10. 在Java中,对 byte 和 short 类型 进行位操作的时候,严重留意事项

    总结:在java中,对byte和short类型的 右移操作 必须先进行 & 0xff 后再右移,避免byte或short是负数的情况下,导致 右移操作前 自动升为int,前面补了很多1,此时右 ...