研究原因:学习 .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

  1. public static void Main(string[] args)
  2. {
  3. CreateHostBuilder(args).Build().Run();
  4. }
  5.  
  6. public static IHostBuilder CreateHostBuilder(string[] args) =>
  7. Host.CreateDefaultBuilder(args)
  8. .ConfigureWebHostDefaults(webBuilder =>
  9. {
  10. webBuilder.UseStartup<Startup>();
  11. });

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 中找到这个方法的源码如下

  1. public static IHostBuilder CreateDefaultBuilder(string[] args)
  2. {
  3. var builder = new HostBuilder();
  4.  
  5. builder.UseContentRoot(Directory.GetCurrentDirectory());
  6. builder.ConfigureHostConfiguration(config =>
  7. {
  8. config.AddEnvironmentVariables(prefix: "DOTNET_");
  9. if (args != null)
  10. {
  11. config.AddCommandLine(args);
  12. }
  13. });
  14.  
  15. builder.ConfigureAppConfiguration((hostingContext, config) =>
  16. {
  17. var env = hostingContext.HostingEnvironment;
  18.  
  19. config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
  20. .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
  21.  
  22. if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName))
  23. {
  24. var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
  25. if (appAssembly != null)
  26. {
  27. config.AddUserSecrets(appAssembly, optional: true);
  28. }
  29. }
  30.  
  31. config.AddEnvironmentVariables();
  32.  
  33. if (args != null)
  34. {
  35. config.AddCommandLine(args);
  36. }
  37. })
  38. .ConfigureLogging((hostingContext, logging) =>
  39. {
  40. var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
  41.  
  42. // IMPORTANT: This needs to be added *before* configuration is loaded, this lets
  43. // the defaults be overridden by the configuration.
  44. if (isWindows)
  45. {
  46. // Default the EventLogLoggerProvider to warning or above
  47. logging.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Warning);
  48. }
  49.  
  50. logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
  51. logging.AddConsole();
  52. logging.AddDebug();
  53. logging.AddEventSourceLogger();
  54.  
  55. if (isWindows)
  56. {
  57. // Add the EventLogLoggerProvider on windows machines
  58. logging.AddEventLog();
  59. }
  60. })
  61. .UseDefaultServiceProvider((context, options) =>
  62. {
  63. var isDevelopment = context.HostingEnvironment.IsDevelopment();
  64. options.ValidateScopes = isDevelopment;
  65. options.ValidateOnBuild = isDevelopment;
  66. });
  67.  
  68. return builder;
  69. }

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

指定Host要使用的根目录

  1. builder.UseContentRoot(Directory.GetCurrentDirectory());

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

  1. builder.ConfigureHostConfiguration(config => ......
  2.  
  3. builder.ConfigureAppConfiguration((hostingContext, config) => ......

配置.Net Core 默认的Log

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

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

  1. .UseDefaultServiceProvider((context, options) =>
  2. {
  3. var isDevelopment = context.HostingEnvironment.IsDevelopment();
  4. options.ValidateScopes = isDevelopment;
  5. options.ValidateOnBuild = isDevelopment;
  6. });

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

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

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

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

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

  1. public static class GenericHostBuilderExtensions
  2. {
  3. public static IHostBuilder ConfigureWebHostDefaults(this IHostBuilder builder, Action<IWebHostBuilder> configure)
  4. {
  5. return builder.ConfigureWebHost(webHostBuilder =>
  6. {
  7. WebHost.ConfigureWebDefaults(webHostBuilder);
  8.  
  9. configure(webHostBuilder);
  10. });
  11. }
  12. }

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

  1. var webHostBuilder = new GenericWebHostBuilder(builder);
  2. configure(webHostBuilder);

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

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

  1. webBuilder.UseStartup<Startup>();

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

上半部分Main()

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

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

  1. public IHost Build()
  2. {
  3. if (_hostBuilt)
  4. {
  5. throw new InvalidOperationException("Build can only be called once.");
  6. }
  7. _hostBuilt = true;
  8.  
  9. BuildHostConfiguration();
  10. CreateHostingEnvironment();
  11. CreateHostBuilderContext();
  12. BuildAppConfiguration();
  13. CreateServiceProvider();
  14.  
  15. return _appServices.GetRequiredService<IHost>();
  16. }

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

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

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

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

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

  1. public static async Task RunAsync(this IHost host, CancellationToken token = default)
  2. {
  3. try
  4. {
  5. await host.StartAsync(token);
  6.  
  7. await host.WaitForShutdownAsync(token);
  8. }
  9. finally
  10. {
  11. if (host is IAsyncDisposable asyncDisposable)
  12. {
  13. await asyncDisposable.DisposeAsync();
  14. }
  15. else
  16. {
  17. host.Dispose();
  18. }
  19.  
  20. }
  21. }

总结:

看起来非常复杂,但是经过细细分析,发现其实很简单,就是创建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. [转帖]LVS入门篇(三)之LVS的工作模式和调度算法

    LVS入门篇(三)之LVS的工作模式和调度算法 https://www.cnblogs.com/linuxk/p/9358512.html 1.NAT模型 (1)原理图: ①.客户端(200.10.1 ...

  2. [转帖]linux学习:sed与awk与tr用法整理

    https://www.cnblogs.com/LO-gin/p/6882490.html 流编辑器:sed 语法:sed [-hnV][-e<script>][-f<script文 ...

  3. Docker 23.0.0 简单学习与使用

    前言 Docker 从2013年火起来到现在才第十个年头. 现在已经被Google的K8S打的没有任何还手之力. 随着K8S放弃支持docker,仅支持containerd的方式. 直接导致docke ...

  4. vue2全局路由守卫独享路由守卫组件内路由守卫共5个

    路由守卫的参数介绍 import Vue from "vue"; import VueRouter from "vue-router"; import Home ...

  5. 玩一玩 VictoriaLogs

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 下载 see: https://github.com/Vi ...

  6. 【一】分布式训练---单机多卡多机多卡(飞桨paddle1.8)

    1.分布式训练简介 分布式训练的核心目的: 加快模型的训练速度.通过对训练任务按照一定方法拆分分配到多个计算节点进行计算,再按照一定的方法对需要汇总的信息进行聚合,从而实现加快训练速度的目的. 1.1 ...

  7. Load-balanced-online-OJ-system 负载均衡的OJ系统项目

    前言 那么这里博主先安利一些干货满满的专栏了! 首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助. 高质量博客汇总 本项目Github地址 - ...

  8. webrtc终极版(二)搭建自己的iceserver服务,并用到RTCMultiConnection的demo中

    webrtc终极版(二)搭建自己的iceserver服务,并用到RTCMultiConnection的demo中 目录 webrtc终极版(二)搭建自己的iceserver服务,并用到RTCMulti ...

  9. Delphi dbgrideh颜色设置

    DBGridEh中分行分列.单元格的颜色设置(1)分行不同颜色设置:在DBGridEh1DrawColumnCell中写: if ADOQuery1.RecNo mod 2=0 then begin ...

  10. Power BI 7 DAY

    DAX 表达式(Data Analysis Expressions) DAX表达式的结果应用在数据透视表中 DAX表达式的结果作用于整列或者表中所有行 还需注意以下几点: a. 表名用"'' ...