首页 > 其他分享 >.net core web 启动过程(1)

.net core web 启动过程(1)

时间:2024-08-09 16:20:32浏览次数:13  
标签:core web builder private var services new net public

1.要了解启动过程,先介绍 IHostingStartup和IStartup 接口

/// <summary>
/// Represents platform specific configuration that will be applied to a <see cref="IWebHostBuilder"/> when building an <see cref="IWebHost"/>.
/// </summary>
public interface IHostingStartup
{
    /// <summary>
    /// Configure the <see cref="IWebHostBuilder"/>.
    /// </summary>
    /// <remarks>
    /// Configure is intended to be called before user code, allowing a user to overwrite any changes made.
    /// </remarks>
    /// <param name="builder"></param>
    void Configure(IWebHostBuilder builder);
}
/// <summary>
/// Provides an interface for initializing services and middleware used by an application.
/// </summary>
public interface IStartup
{
    /// <summary>
    /// Register services into the <see cref="IServiceCollection" />.
    /// </summary>
    /// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
    IServiceProvider ConfigureServices(IServiceCollection services);

    /// <summary>
    /// Configures the application.
    /// </summary>
    /// <param name="app">An <see cref="IApplicationBuilder"/> for the app to configure.</param>
    void Configure(IApplicationBuilder app);
}
从上面2个接口可以看出,IHostingStartup 比 IStartup 不同点是缺少 IServiceProvider ConfigureServices(IServiceCollection services);
其实二者的运行顺序也是不同,稍后会有详细介绍。
IHostingStartup 用以配置 IWebHostBuilder,.net core 允许有多个 IHostingStartup来实现在不同得地方配置 WebHostBuilder,下面展示一下 IHostingStartup得调用过程。
项目启动首先会执行Program.cs文件中的 Main方法,Program文件只是默认项,可以通过更改项目配置 来实现调用指定类的Main方法,
namespace SampleStartups;
public class Program
{
    public static void Main(string[] args)
    {
       
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args)
    {
        return new WebHostBuilder()
            .UseKestrel(opt =>
            {
                opt.AddServerHeader = false;
                opt.Limits.MaxRequestLineSize = 16 * 1024;
            })
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseStartup<InjectedStartup>(); 
    }
}

例1:例子来源.net core 8.0 源码 中 Hosting 的SampleStartups

项目配置

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
    <StartupObject>SampleStartups.StartupBlockingOnStart</StartupObject>
    <OutputType>exe</OutputType>
  </PropertyGroup>

  <ItemGroup>
    <Reference Include="Microsoft.AspNetCore.Hosting" />
    <Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
    <Reference Include="Microsoft.Extensions.Configuration.CommandLine" />
    <Reference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
    <Reference Include="Microsoft.Extensions.Configuration.Json" />
  </ItemGroup>
</Project>

通过配置 <StartupObject></StartupObject> 程序启动文件为 SampleStartups.StartupBlockingOnStart

namespace SampleStartups;

public class StartupInjection : IHostingStartup
{
    public void Configure(IWebHostBuilder builder)
    {
        builder.UseStartup<InjectedStartup>();
    }

    // Entry point for the application.
    public static Task Main(string[] args)
    {
        var host = new HostBuilder()
            .ConfigureWebHost(webHostBuilder =>
            {
                webHostBuilder
                    .UseKestrel()
                    // Each of these three sets ApplicationName to the current assembly, which is needed in order to
                    // scan the assembly for HostingStartupAttributes.
                    // .UseSetting(WebHostDefaults.ApplicationKey, "SampleStartups")
                    // .Configure(_ => { })
                    .UseStartup<NormalStartup>();
            })
            .Build();

        return host.RunAsync();
    }
}

 

HostBuilder.ConfigureXXX

这里优先介绍HostBuilder一下ConfigureHostConfigurationConfigureAppConfigurationConfigureServices等方法到底做了什么。其实就是将Action暂存到了一个私有属性变量中,并没有立即执行。先存在一个list中,后边按一定顺序执行。稍后会介绍在哪里执行这些Action

public class HostBuilder : IHostBuilder
{
    private List<Action<IConfigurationBuilder>> _configureHostConfigActions = new List<Action<IConfigurationBuilder>>();
    private List<Action<HostBuilderContext, IConfigurationBuilder>> _configureAppConfigActions = new List<Action<HostBuilderContext, IConfigurationBuilder>>();
    private List<Action<HostBuilderContext, IServiceCollection>> _configureServicesActions = new List<Action<HostBuilderContext, IServiceCollection>>();

    public IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate)
    {
        _configureHostConfigActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
        return this;
    }
    
    public IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate)
    {
        _configureAppConfigActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
        return this;
    }
        
    public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate)
    {
        _configureServicesActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
        return this;
    }
}

我们看一下  new HostBuilder().ConfigureWebHost(Action<IWebHostBuilder>  configure) 中  ConfigureWebHost() 方法中具体执行了什么  ,

private static IHostBuilder ConfigureWebHost(
        this IHostBuilder builder,
        Func<IHostBuilder, WebHostBuilderOptions, IWebHostBuilder> createWebHostBuilder,
        Action<IWebHostBuilder> configure,
        Action<WebHostBuilderOptions> configureWebHostBuilder)
    {
        ArgumentNullException.ThrowIfNull(configure);
        ArgumentNullException.ThrowIfNull(configureWebHostBuilder);

     //先不介绍这里
        // Light up custom implementations namely ConfigureHostBuilder which throws.
        if (builder is ISupportsConfigureWebHost supportsConfigureWebHost)
        {
            return supportsConfigureWebHost.ConfigureWebHost(configure, configureWebHostBuilder);
        }
     //重点看下这里
        var webHostBuilderOptions = new WebHostBuilderOptions();
        configureWebHostBuilder(webHostBuilderOptions);
     //这里创建了webhostBuilder对象,并立即执行传入的action
        var webhostBuilder = createWebHostBuilder(builder, webHostBuilderOptions);
     //执行传入的action
        configure(webhostBuilder);
     //此处暂存了一个action ,其实在  createWebHostBuilder(builder, webHostBuilderOptions) 中已经执行了逻辑,暂存多个关键的action
     builder.ConfigureServices((context, services) => services.AddHostedService<GenericWebHostService>());
     return builder; 
  }

 

我们看一下在  new GenericWebHostBuilder(hostBuilder, options)  中执行的逻辑

 

internal sealed class GenericWebHostBuilder : WebHostBuilderBase, ISupportsStartup
{
   //用于暂存关于IStartup的相关类型,
    private object? _startupObject;
    private readonly object _startupKey = new object();

    private AggregateException? _hostingStartupErrors;
    private HostingStartupWebHostBuilder? _hostingStartupWebHostBuilder;

    public GenericWebHostBuilder(IHostBuilder builder, WebHostBuilderOptions options)
        : base(builder, options)
    {
     //暂存一个HostConfigration的action 
      
        _builder.ConfigureHostConfiguration(config =>
        {
            config.AddConfiguration(_config);
       //重点关注下这里,稍后介绍
            // We do this super early but still late enough that we can process the configuration
            // wired up by calls to UseSetting
            ExecuteHostingStartups();
        });
        //暂存
        // IHostingStartup needs to be executed before any direct methods on the builder
        // so register these callbacks first
        _builder.ConfigureAppConfiguration((context, configurationBuilder) =>
        {
            if (_hostingStartupWebHostBuilder != null)
            {
                var webhostContext = GetWebHostBuilderContext(context);
                _hostingStartupWebHostBuilder.ConfigureAppConfiguration(webhostContext, configurationBuilder);
            }
        });
    //暂存CongfigureServices action
        _builder.ConfigureServices((context, services) =>
        {
            var webhostContext = GetWebHostBuilderContext(context);
            var webHostOptions = (WebHostOptions)context.Properties[typeof(WebHostOptions)];

            // Add the IHostingEnvironment and IApplicationLifetime from Microsoft.AspNetCore.Hosting
            services.AddSingleton(webhostContext.HostingEnvironment);
#pragma warning disable CS0618 // Type or member is obsolete
            services.AddSingleton((AspNetCore.Hosting.IHostingEnvironment)webhostContext.HostingEnvironment);
            services.AddSingleton<IApplicationLifetime, GenericWebHostApplicationLifetime>();
#pragma warning restore CS0618 // Type or member is obsolete

            services.Configure<GenericWebHostServiceOptions>(options =>
            {
                // Set the options
                options.WebHostOptions = webHostOptions;
                // Store and forward any startup errors
                options.HostingStartupExceptions = _hostingStartupErrors;
            });

            // REVIEW: This is bad since we don't own this type. Anybody could add one of these and it would mess things up
            // We need to flow this differently
            services.TryAddSingleton(sp => new DiagnosticListener("Microsoft.AspNetCore"));
            services.TryAddSingleton<DiagnosticSource>(sp => sp.GetRequiredService<DiagnosticListener>());
            services.TryAddSingleton(sp => new ActivitySource("Microsoft.AspNetCore"));
            services.TryAddSingleton(DistributedContextPropagator.Current);

            services.TryAddSingleton<IHttpContextFactory, DefaultHttpContextFactory>();
            services.TryAddScoped<IMiddlewareFactory, MiddlewareFactory>();
            services.TryAddSingleton<IApplicationBuilderFactory, ApplicationBuilderFactory>();

            services.AddMetrics();
            services.TryAddSingleton<HostingMetrics>();

            // IMPORTANT: This needs to run *before* direct calls on the builder (like UseStartup)
            _hostingStartupWebHostBuilder?.ConfigureServices(webhostContext, services);
        //如果特别指定了startupAssembly ,程序就会扫描程序集获取,这个行为也被暂存在当前的这个action 
            // Support UseStartup(assemblyName)
            if (!string.IsNullOrEmpty(webHostOptions.StartupAssembly))
            {
                ScanAssemblyAndRegisterStartup(context, services, webhostContext, webHostOptions);
            }
        });
    }

  //删除部分代码
}

在webHostBuilder创建过程中暂存了ConfigureHostConfiguration,ConfigureAppConfiguration,ConfigureServices,那么他们都在什么时候执行,并且是一个什么执行顺序。这就要看一下他们在HostBuilder 中Build过程中的执行顺序了,不废话,直接上代码

public class HostBuilder : IHostBuilder
{
    public IHost Build()
    {
        // 加载主机(Host)配置
        BuildHostConfiguration();
        // 实例化 HostingEnvironment
        CreateHostingEnvironment();
        // 实例化 HostBuilderContext
        CreateHostBuilderContext();
        // 加载应用(App)配置
        BuildAppConfiguration();
        // 注册服务并创建 Service Provider
        CreateServiceProvider();
    
        // 生成 IHost 实例并返回
        return _appServices.GetRequiredService<IHost>();
    }
}

 

不难看出 在 BuildHostConfiguration()方法中,会执行在上面介绍 new WebHostBuilder() 构造函数中暂存的一个action ,看下方代码暂存的那个action

 //暂存一个HostConfigration的action 
      
        _builder.ConfigureHostConfiguration(config =>
        {
            config.AddConfiguration(_config);
       //重点关注下这里,稍后介绍
            // We do this super early but still late enough that we can process the configuration
            // wired up by calls to UseSetting
            ExecuteHostingStartups();
        });

 

此时为执行 ExecuteHostingStartups,此处涉及执行 HostStartup相关逻辑。

internal sealed class GenericWebHostBuilder : WebHostBuilderBase, ISupportsStartup
{
    private object? _startupObject;
    private readonly object _startupKey = new object();

    private AggregateException? _hostingStartupErrors;
    private HostingStartupWebHostBuilder? _hostingStartupWebHostBuilder;

   private void ExecuteHostingStartups()
   {
       var webHostOptions = new WebHostOptions(_config);

       if (webHostOptions.PreventHostingStartup)
       {
           return;
       }

       var exceptions = new List<Exception>();
       var processed = new HashSet<Assembly>();

       _hostingStartupWebHostBuilder = new HostingStartupWebHostBuilder(this);

       // Execute the hosting startup assemblies
       foreach (var assemblyName in webHostOptions.GetFinalHostingStartupAssemblies())
       {
           try
           {
               var assembly = Assembly.Load(new AssemblyName(assemblyName));

               if (!processed.Add(assembly))
               {
                   // Already processed, skip it
                   continue;
               }
         //找出有标记HostingStartupAttribute的信息
               foreach (var attribute in assembly.GetCustomAttributes<HostingStartupAttribute>())
               {
           //根据标签中指定的类型,类型需要继承IHostSrartup接口 ,实例化该类型的对象,然后执行对象Configue方法 来配置WebHostBuilder
                   var hostingStartup = (IHostingStartup)Activator.CreateInstance(attribute.HostingStartupType)!;
                   hostingStartup.Configure(_hostingStartupWebHostBuilder);
               }
           }
           catch (Exception ex)
           {
               // Capture any errors that happen during startup
               exceptions.Add(new InvalidOperationException($"Startup assembly {assemblyName} failed to execute. See the inner exception for more details.", ex));
           }
       }

       if (exceptions.Count > 0)
       {
           _hostingStartupErrors = new AggregateException(exceptions);
       }
   }
}

 

 总结:

IHostStartup 是在HostBuild的BuildHostConfiguration()方法中执行,执行顺序早于 
CreateHostingEnvironment()、CreateHostBuilderContext,BuildAppConfiguration(),CreateServiceProvider();






标签:core,web,builder,private,var,services,new,net,public
From: https://www.cnblogs.com/hitx/p/18350927

相关文章

  • .net core web 启动过程(2)
    在.netcoreweb启动过程(1)中介绍了IHostStartup的执行过程,该文章主要介绍IStartup的执行过程。最常用的配置Startup方式,通过调用webHostBuilder扩展方法UseStartup<T>来指定。varhost=newHostBuilder().ConfigureWebHost(webHostBuilder=>{......
  • Nuget 管理器》》 error: NU1101: 找不到包 ViewFaceCore
    error:NU1101:找不到包ViewFaceCore错误解释:NU1101错误表示NuGet无法找到名为ViewFaceCore的包。这通常意味着包不存在于指定的源中,或者包名称拼写错误。解决方法:检查包名称:确保ViewFaceCore是正确的包名,没有拼写错误。检查源:确保你的NuGet配置包含了......
  • ssm+vue基于VUE的Web购物网站的设计与开发【开题+程序+论文】-计算机毕业设计
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着互联网技术的飞速发展,电子商务已成为全球经济的重要组成部分,深刻改变了人们的消费习惯与商业模式。购物网站作为电子商务的核心载体,不仅为消费者......
  • Kubernetes对象YAML文件的基本格式详解
    简介  Kubernetes(K8s)作为云原生时代的基础设施核心,其配置文件通常采用YAML格式来定义和管理各种资源对象。YAML(YAMLAin'tMarkupLanguage)因其简洁、易读和易写的特性,在Kubernetes中得到了广泛应用。本文将详细探讨Kubernetes对象YAML文件的基本格式,重点解析GVK(Group、Ve......
  • com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: can not find lamb
    异常提示:com.baomidou.mybatisplus.core.exceptions.MybatisPlusException:cannotfindlambdacacheforthisentity 使用mockito框架做单元测试:mybatisplus使用Lambda表达式做条件查询、条件更新时会遇到mybatis拿不到缓存问题:错误1:com.baomidou.mybatisplus......
  • 基于 go-zero 框架的项目中集成 WebSocket
    WebSocket集成指南本文档描述了如何在基于go-zero框架的项目中集成WebSocket。1.安装依赖首先,安装gorilla/websocket库:gogetgithub.com/gorilla/websocket2.项目结构在项目中添加以下文件和目录:└──pkg└──websocket└──websocket.go3......
  • 医学图像分割的基准:TransUnet(用于医学图像分割的Transformer编码器)器官分割
    1、TransUnet介绍TransUnet是一种用于医学图像分割的深度学习模型。它是基于Transformer模型的图像分割方法,由AI研究公司HuggingFace在2021年提出。医学图像分割是一项重要的任务,旨在将医学图像中的不同结构和区域分离出来,以便医生可以更好地诊断和治疗疾病。传统的医学......
  • Stable Diffusion WebUI v1.10.0重大更新,支持SD3!
    Hello,大家好!前不久,SDWebUI的作者AUTOMATIC1111终于把它更新到了v1.10.0,这次不仅修复以往的一些BUG,提升了一些性能,这次还支持了SD3_medium.safetensors模型以及SD3_LoRA模型,同时还支持T5系列的encoder模型,让我们一起来看看这次更新了哪些内容。更新内容总共有87项更新:1.......
  • 零代码连接 OneNet 只需三分钟!一个安卓 APP 搞定 OneNet 物模型数据刷新与显示
    前言在物联网(IoT)开发中,快速连接设备与云平台、实现数据的实时刷新与显示,是开发者常常遇到的挑战。为此本文将展示如何在短短三分钟内,通过一个安卓APP轻松实现与OneNet的连接,并展示物模型数据。无论你是初学者还是有经验的开发者,这个简单的方法都能助你快速上手。什么......
  • .NET 8 + Blazor 多租户、模块化、DDD框架、开箱即用
    前言基于.NET8的开源项目,主要使用WebAPI+Blazor支持多租户和模块化设计,DDD构建。可以帮助我们轻松地搭建起一个功能完善的Web应用程序。除了帮助你快速构建应用程序之外,项目也可以当做学习资料。我们可以从中了解到多租户、CQRS、DDD架构、云部署、Docker容器化等等前沿技......