.net core 源码分析(3) 启动过程-InitializeHostConfiguration

关于Hosting展示代码来源 .net core的runtime-8.0.7源码:https://github.com/dotnet/runtime

/// <summary>
 /// A program initialization utility.
 /// </summary>
 public partial class HostBuilder : IHostBuilder
 {
     private const string HostBuildingDiagnosticListenerName = "Microsoft.Extensions.Hosting";
     private const string HostBuildingEventName = "HostBuilding";
     private const string HostBuiltEventName = "HostBuilt";
   //这部分代码在本系列文章第一章已经描述,此处不过多介绍
     private readonly List<Action<IConfigurationBuilder>> _configureHostConfigActions = new List<Action<IConfigurationBuilder>>();
     private readonly List<Action<HostBuilderContext, IConfigurationBuilder>> _configureAppConfigActions = new List<Action<HostBuilderContext, IConfigurationBuilder>>();
     private readonly List<Action<HostBuilderContext, IServiceCollection>> _configureServicesActions = new List<Action<HostBuilderContext, IServiceCollection>>();
     
     private readonly List<IConfigureContainerAdapter> _configureContainerActions = new List<IConfigureContainerAdapter>();

     private IServiceFactoryAdapter _serviceProviderFactory;
   //这些在属性会在Build()方法中不同阶段中被创建
     private bool _hostBuilt;
     private IConfiguration? _hostConfiguration;
     private IConfiguration? _appConfiguration;
     private HostBuilderContext? _hostBuilderContext;
     private HostingEnvironment? _hostingEnvironment;
     private IServiceProvider? _appServices;
     private PhysicalFileProvider? _defaultProvider;

     /// <summary>
     /// Initializes a new instance of <see cref="HostBuilder"/>.
     /// </summary>
     public HostBuilder()
     {
      //构造函数中默认 初始化 ServiceProviderFactory,不过可以通过HostBuilder.UseServiceProviderFactory方法覆盖掉 
      _serviceProviderFactory = new ServiceFactoryAdapter<IServiceCollection>(new DefaultServiceProviderFactory()); 
    } 
}

 再来回顾一下HostBuilder的Build过程中都经历了哪些阶段。

  public partial class HostBuilder : IHostBuilder
  {
      /// <summary>
      /// Run the given actions to initialize the host. This can only be called once.
      /// </summary>
      /// <returns>An initialized <see cref="IHost"/></returns>
      /// <remarks>Adds basic services to the host such as application lifetime, host environment, and logging.</remarks>
      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 DiagnosticListener diagnosticListener = LogHostBuilding(this);

          // 1.初始化主机(Host)配置
          InitializeHostConfiguration();
          // 2.初始化 HostingEnvironment
          InitializeHostingEnvironment();
          // 3.初始化 HostBuilderContext
          InitializeHostBuilderContext();
          // 4.初始化应用(App)配置
          InitializeAppConfiguration();
          // 5.初始化服务并创建 Service Provider
          InitializeServiceProvider();

          return ResolveHost(_appServices, diagnosticListener);
      }
}

 首先看一下第一阶段初始化HostConfiguration

 [MemberNotNull(nameof(_hostConfiguration))]
 private void InitializeHostConfiguration()
 {
     IConfigurationBuilder configBuilder = new ConfigurationBuilder()
         .AddInMemoryCollection(); // Make sure there's some default storage since there are no default providers

     foreach (Action<IConfigurationBuilder> buildAction in _configureHostConfigActions)
     {
         buildAction(configBuilder);
     } 
    
  
    //此阶段结束,私有属性 _hostConfiguration已经被创建,在第二个阶段中就可以使用了。  _hostConfiguration = configBuilder.Build();
}

 InitializeHostConfiguration()主要用以初始化系统的配置对象,通过 ConfigurationBuilder 来构建 ICongfigurationRoot, 下面展示一下 Configuration模块在源码中的项目结构。

ConfigurationBuilder 类的主要职责

ConfigurationBuilder 类用于构建和初始化 IConfigurationRoot 实例,该实例随后用于应用程序中以访问配置数据。ConfigurationBuilder 通过链式方法允许你添加配置源(如appsettings.json、环境变量、命令行参数等),然后你可以通过 Build() 方法来生成最终的配置对象。配置源:CommandLine、EnvironmentVariables、Ini、JSon、Xml、UserSecrets等。

如果我们自己设计配置系统

试想一下,如果让我们来设计配置信息系统, 用户可以多种方式配置信息,可以通过xml文件,json文件,也可以把数据库中的数据读取出来放入配置信息中,读取电脑本地环境变量存在到配置信息中。既然多种类型的配置信息都可能存在,那么我们首先就会想到Provider,每种类型一个Provider .

暂且我们就设计JsonProvider ,XmlProvider,EnvirmentVarianlesProvider , 我们还需要一个管理类来存放用户注册的Provider , 我们把他叫做 ProviderManager,ProviderManager提供一个AddProvider(IProvider)。

ProviderManager.AddProvider(new JsonProvider())

但是这时候我们才发现,在new JsonProvider(),我们需要在构造函数中添加额外的信息,比如文件地址或者其他配置,每个Provider都有可能需要自己相关的初始化信息。

ProviderManager.AddProvider(new JsonProvider( string fileName, xxxx))
ProviderManager.AddProvider(new MemoryProvider(string xxx))

来看一下JsonProvider真实的添加情况

   /// <summary>
   /// Represents a JSON file as an <see cref="IConfigurationSource"/>.
   /// </summary>
   public class JsonConfigurationSource : FileConfigurationSource
   {
       /// <summary>
       /// Builds the <see cref="JsonConfigurationProvider"/> for this source.
       /// </summary>
       /// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
       /// <returns>A <see cref="JsonConfigurationProvider"/></returns>
       public override IConfigurationProvider Build(IConfigurationBuilder builder)
       {
           EnsureDefaults(builder);
           return new JsonConfigurationProvider(this);
       }
   }

这里在new  JsonConfigurationProvider运行了部分逻辑EnsureDefaults,JsonConfigurationProvider 构造函数参数也不仅仅需要一个字符串这种简单的参数,显然这种情况如果每次用户添加Provider 的时候需要显示的执行特定逻辑不是那么适合,所以我们要升级我们的设计,   我们不直接添加Provider ,我们添加ProviderBuilder 来隐藏new provider 的过程。ProviderBuilder也可以称为ProviderFactory.总之该类的工作就是创建对应的Provider。所以我们添加配置的设计改为

//升级前
ProviderManager.AddProvider(new JsonProvider( string fileName, xxxx))
ProviderManager.AddProvider(new MemoryProvider(string xxx))


//升级后
ProviderManager.Add(new JsonProviderBuilder( string fileName, xxxx))

.net core 中把ProviderBuilder 抽象为IConfigurationSource 

  /// <summary>
  /// Represents a source of configuration key/values for an application.
  /// </summary>
  public interface IConfigurationSource
  {
      /// <summary>
      /// Builds the <see cref="IConfigurationProvider"/> for this source.
      /// </summary>
      /// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
      /// <returns>An <see cref="IConfigurationProvider"/></returns>
      IConfigurationProvider Build(IConfigurationBuilder builder);
  }
 /// <summary>
 /// Represents environment variables as an <see cref="IConfigurationSource"/>.
 /// </summary>
 public class EnvironmentVariablesConfigurationSource : IConfigurationSource
 {
     /// <summary>
     /// A prefix used to filter environment variables.
     /// </summary>
     public string? Prefix { get; set; }

     /// <summary>
     /// Builds the <see cref="EnvironmentVariablesConfigurationProvider"/> for this source.
     /// </summary>
     /// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
     /// <returns>A <see cref="EnvironmentVariablesConfigurationProvider"/></returns>
     public IConfigurationProvider Build(IConfigurationBuilder builder)
     {
         return new EnvironmentVariablesConfigurationProvider(Prefix);
     }
 }

下面来看一下ConfigurationBuilder的Build方法,

 public class ConfigurationBuilder : IConfigurationBuilder
    {
        private readonly List<IConfigurationSource> _sources = new();

     //前面介绍的AddProvider的地方换成AddProviderBuilder
    

        /// <summary>
        /// Returns the sources used to obtain configuration values.
        /// </summary>
        public IList<IConfigurationSource> Sources => _sources;
      
        /// <summary>
        /// Gets a key/value collection that can be used to share data between the <see cref="IConfigurationBuilder"/>
        /// and the registered <see cref="IConfigurationProvider"/>s.
        /// </summary>
        public IDictionary<string, object> Properties { get; } = new Dictionary<string, object>();
    
    

        /// <summary>
        /// Adds a new configuration source.
     /// 用户注册各种不同的配置信息,json文件的,xml的,环境变量的
        /// </summary>
        /// <param name="source">The configuration source to add.</param>
        /// <returns>The same <see cref="IConfigurationBuilder"/>.</returns>
        public IConfigurationBuilder Add(IConfigurationSource source)
        {
            ThrowHelper.ThrowIfNull(source);

            _sources.Add(source);
            return this;
        }

        /// <summary>
        /// Builds an <see cref="IConfiguration"/> with keys and values from the set of providers registered in
        /// <see cref="Sources"/>.
        /// </summary>
     ///Build过程中,每个Provider就会进行初始化配置信息的逻辑,比如Json文件的Provider就读取josn文件内容到内存中,环境变量的Provider就读取本地环境变量到内存中

        /// <returns>An <see cref="IConfigurationRoot"/> with keys and values from the registered providers.</returns>
        public IConfigurationRoot Build()
        {
            var providers = new List<IConfigurationProvider>();
            foreach (IConfigurationSource source in _sources)
            {
                IConfigurationProvider provider = source.Build(this);
                providers.Add(provider);
            }
        //所有的Provider都被放在ConfigurationRoot中,
        //每个Provider中持有一个私有字典 用于存放 配置信息,key-value

            return new ConfigurationRoot(providers);
        }
    }

到了这里相信大家已经弄明白.net core 配置文件是怎么回事了。

 

 

 

书接上回

所有说在HostBuilder.Build的第一阶段 InitializeHostConfiguration(); 核心主要是进行了配置文件的初始化,构造了 _hostConfiguration = new ConfigurationRoot(providers);

 回顾一下HostBuild暂存的 List<Action<IConfigurationBuilder>> _configureHostConfigActions ,可以通过执行 Action 实现添加ConfigurationSource到ConfigurationBuilder中 

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;
    }
}

 

.net core 源码分析

posted @ 2024-08-09 14:33  Hi同学  阅读(21)  评论(0编辑  收藏  举报