.net core web项目源码学习,循序渐进分析(1)

先初始化一个.net core 5的webapi项目。

项目初始时候文件结构如下图:
在这里插入图片描述

1. 首先

首先,从Program.cs文件中的Main函数开始,这是最没有疑义的了。
如有,原因就很简单:Just it is

2. 开始分析Main函数

main函数内容如图:

public class Program
{
		//注释1
      public static void Main(string[] args)
      {
          CreateHostBuilder(args).Build().Run();
      }
		//注释2
      public static IHostBuilder CreateHostBuilder(string[] args) =>
          Host.CreateDefaultBuilder(args)
              .ConfigureWebHostDefaults(webBuilder =>
              {
                  webBuilder
                  .Inject()
                  .UseStartup<Startup>();
              });
  }

显然,main里面直接调用了注释2的这个方法。

那么注释1可以怎么理解
如果把注释1的代码换一种写法,只学过一天代码的小白也会迅速理解的。
必要的知识储备:建造者模式
其中,CreateHostBuilder(args).Build() 就是建造者模式的常见写法而已,用于最终创建一个对象实例。

	public static void Main(string[] args)
    {
        IHostBuilder hostBuilder = CreateHostBuilder(args);
        IHost host = hostBuilder.Build();
        host.Run();
    }

那么注释2是个什么?
是个匿名函数。
换一个写法:

//是注释2的简单易懂版。
public static IHostBuilder CreateHostBuilder(string[] args)
 {
 		//创建默认的IHostBuilder
      IHostBuilder hostBuilder = Host.CreateDefaultBuilder(args);
	  
	  //显然此处是关键部分,注释3
      hostBuilder.ConfigureWebHostDefaults(webBuilder =>
      {
            webBuilder
            .Inject()
            .UseStartup<Startup>();
        });

         return hostBuilder;
  }

由此可知,在main里得到一个 IHostBuildr 实例,然后 .Build()[xxxBuilder.Build() 是 建造者模式的特有写法,就是创建实例。],然后是.Run(),如名称所示,含义很清楚的了。

而且, ConfigureWebHostDefaults() 这个方法的意思就是: 接收上文产生的一个 IHostBuilder 实例,并且继续给它调用 Action<IWebHostBuilder> 参数。

注释3处相关的参考信息:

参考地址: ConfigureWebHostDefaults 方法的分析

上面文档中的关键之处:

关键处:
builder
IHostBuilder
The IHostBuilder instance to configure.

configure
Action<IWebHostBuilder>
The configure callback

然后看看源码(注意找到源码不是一件容易的事):
在这里插入图片描述
源码出处:https://github.com/dotnet/aspnetcore.git

[上面一图的代码出自上面的链接]
文件地址:aspnetcore\src\DefaultBuilder\src\GenericHostBuilderExtensions.cs L32

源码出处2:https://github.com/dotnet/runtime.git
源码出处3:https://github.com/aspnet/Hosting.git
以上三个项目,是我找到的比较有用的M$官方源码,GitHub。

由上图的绿框,蓝框可知,是先创建了一个Default的WebHostBuilder,再 把这个WebHostBuilder交给参数。
也就是这个WebHostBuilder又执行了UseStartup<Startup>()方法。

3 再分析分析UseStartup()

 webBuilder.UseStartup<Startup>();

中的 UserStartUp 和 怎么解读?

3.1 先上UseStartup源码

在这里插入图片描述
源码出处:
在刚才提到的源码出处1的项目中,路径如下:
aspnetcore\src\Hosting\Hosting\src\WebHostBuilderExtensions.cs

3.2 再向深处跟踪

继续向里跳转(L92行的方法往里找):

aspnetcore\src\Hosting\Hosting\src\GenericHost\GenericWebHostBuilder.cs L243

 [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2006:UnrecognizedReflectionPattern", Justification = "We need to call a generic method on IHostBuilder.")]
        private void UseStartup([DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType, HostBuilderContext context, IServiceCollection services, object? instance = null)
        {
            var webHostBuilderContext = GetWebHostBuilderContext(context);
            var webHostOptions = (WebHostOptions)context.Properties[typeof(WebHostOptions)];

            ExceptionDispatchInfo? startupError = null;
            ConfigureBuilder? configureBuilder = null;

            try
            {
                // We cannot support methods that return IServiceProvider as that is terminal and we need ConfigureServices to compose
                if (typeof(IStartup).IsAssignableFrom(startupType))
                {
                    throw new NotSupportedException($"{typeof(IStartup)} isn't supported");
                }
                if (StartupLoader.HasConfigureServicesIServiceProviderDelegate(startupType, context.HostingEnvironment.EnvironmentName))
                {
                    throw new NotSupportedException($"ConfigureServices returning an {typeof(IServiceProvider)} isn't supported.");
                }

                instance ??= ActivatorUtilities.CreateInstance(new HostServiceProvider(webHostBuilderContext), startupType);
                context.Properties[_startupKey] = instance;

                // Startup.ConfigureServices
                var configureServicesBuilder = StartupLoader.FindConfigureServicesDelegate(startupType, context.HostingEnvironment.EnvironmentName);
                var configureServices = configureServicesBuilder.Build(instance);

                configureServices(services);

                // REVIEW: We're doing this in the callback so that we have access to the hosting environment
                // Startup.ConfigureContainer
                var configureContainerBuilder = StartupLoader.FindConfigureContainerDelegate(startupType, context.HostingEnvironment.EnvironmentName);
                if (configureContainerBuilder.MethodInfo != null)
                {
                    var containerType = configureContainerBuilder.GetContainerType();
                    // Store the builder in the property bag
                    _builder.Properties[typeof(ConfigureContainerBuilder)] = configureContainerBuilder;

                    var actionType = typeof(Action<,>).MakeGenericType(typeof(HostBuilderContext), containerType);

                    // Get the private ConfigureContainer method on this type then close over the container type
                    var configureCallback = typeof(GenericWebHostBuilder).GetMethod(nameof(ConfigureContainerImpl), BindingFlags.NonPublic | BindingFlags.Instance)!
                                                     .MakeGenericMethod(containerType)
                                                     .CreateDelegate(actionType, this);

                    // _builder.ConfigureContainer<T>(ConfigureContainer);
                    typeof(IHostBuilder).GetMethod(nameof(IHostBuilder.ConfigureContainer))!
                        .MakeGenericMethod(containerType)
                        .InvokeWithoutWrappingExceptions(_builder, new object[] { configureCallback });
                }

                // Resolve Configure after calling ConfigureServices and ConfigureContainer
                configureBuilder = StartupLoader.FindConfigureDelegate(startupType, context.HostingEnvironment.EnvironmentName);
            }
            catch (Exception ex) when (webHostOptions.CaptureStartupErrors)
            {
                startupError = ExceptionDispatchInfo.Capture(ex);
            }

            // Startup.Configure
            services.Configure<GenericWebHostServiceOptions>(options =>
            {
                options.ConfigureApplication = app =>
                {
                    // Throw if there was any errors initializing startup
                    startupError?.Throw();

                    // Execute Startup.Configure
                    if (instance != null && configureBuilder != null)
                    {
                        configureBuilder.Build(instance)(app);
                    }
                };
            });
        }

在这里插入图片描述
看,上图。
获取到Startup的具体class,然后还是对这个原来一路走来的 IWebHostBuilder,先 ConfigServices(),再 Config()
至此就全部通了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值