• netcore3.0 IHost 源码解析(一)


    Nuget包:以Microsoft.Extensins.Hosting开头的Nuget包

    Github地址:https://github.com/dotnet/extensions/tree/master/src/Hosting

    先看下几个重要的接口

     IHostBuilder的实现类HostBuilder

    /// <summary>
        /// A program initialization utility.
        /// </summary>
        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>>();
            private List<IConfigureContainerAdapter> _configureContainerActions = new List<IConfigureContainerAdapter>();
            private IServiceFactoryAdapter _serviceProviderFactory = new ServiceFactoryAdapter<IServiceCollection>(new DefaultServiceProviderFactory());
            private bool _hostBuilt;
            private IConfiguration _hostConfiguration;
            private IConfiguration _appConfiguration;
            private HostBuilderContext _hostBuilderContext;
            private HostingEnvironment _hostingEnvironment;
            private IServiceProvider _appServices;
    
            /// <summary>
            /// A central location for sharing state between components during the host building process.
            /// </summary>
            public IDictionary<object, object> Properties { get; } = new Dictionary<object, object>();
    
            /// <summary>
            /// Set up the configuration for the builder itself. This will be used to initialize the <see cref="IHostEnvironment"/>
            /// for use later in the build process. This can be called multiple times and the results will be additive.
            /// </summary>
            /// <param name="configureDelegate">The delegate for configuring the <see cref="IConfigurationBuilder"/> that will be used
            /// to construct the <see cref="IConfiguration"/> for the host.</param>
            /// <returns>The same instance of the <see cref="IHostBuilder"/> for chaining.</returns>
            public IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate)
            {
                _configureHostConfigActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
                return this;
            }
    
            /// <summary>
            /// Sets up the configuration for the remainder of the build process and application. This can be called multiple times and
            /// the results will be additive. The results will be available at <see cref="HostBuilderContext.Configuration"/> for
            /// subsequent operations, as well as in <see cref="IHost.Services"/>.
            /// </summary>
            /// <param name="configureDelegate">The delegate for configuring the <see cref="IConfigurationBuilder"/> that will be used
            /// to construct the <see cref="IConfiguration"/> for the host.</param>
            /// <returns>The same instance of the <see cref="IHostBuilder"/> for chaining.</returns>
            public IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate)
            {
                _configureAppConfigActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
                return this;
            }
    
            /// <summary>
            /// Adds services to the container. This can be called multiple times and the results will be additive.
            /// </summary>
            /// <param name="configureDelegate">The delegate for configuring the <see cref="IConfigurationBuilder"/> that will be used
            /// to construct the <see cref="IConfiguration"/> for the host.</param>
            /// <returns>The same instance of the <see cref="IHostBuilder"/> for chaining.</returns>
            public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate)
            {
                _configureServicesActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
                return this;
            }
    
            /// <summary>
            /// Overrides the factory used to create the service provider.
            /// </summary>
            /// <typeparam name="TContainerBuilder">The type of the builder to create.</typeparam>
            /// <param name="factory">A factory used for creating service providers.</param>
            /// <returns>The same instance of the <see cref="IHostBuilder"/> for chaining.</returns>
            public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory)
            {
                _serviceProviderFactory = new ServiceFactoryAdapter<TContainerBuilder>(factory ?? throw new ArgumentNullException(nameof(factory)));
                return this;
            }
    
            /// <summary>
            /// Overrides the factory used to create the service provider.
            /// </summary>
            /// <param name="factory">A factory used for creating service providers.</param>
            /// <typeparam name="TContainerBuilder">The type of the builder to create.</typeparam>
            /// <returns>The same instance of the <see cref="IHostBuilder"/> for chaining.</returns>
            public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory)
            {
                _serviceProviderFactory = new ServiceFactoryAdapter<TContainerBuilder>(() => _hostBuilderContext, factory ?? throw new ArgumentNullException(nameof(factory)));
                return this;
            }
    
            /// <summary>
            /// Enables configuring the instantiated dependency container. This can be called multiple times and
            /// the results will be additive.
            /// </summary>
            /// <typeparam name="TContainerBuilder">The type of the builder to create.</typeparam>
            /// <param name="configureDelegate">The delegate for configuring the <see cref="IConfigurationBuilder"/> that will be used
            /// to construct the <see cref="IConfiguration"/> for the host.</param>
            /// <returns>The same instance of the <see cref="IHostBuilder"/> for chaining.</returns>
            public IHostBuilder ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext, TContainerBuilder> configureDelegate)
            {
                _configureContainerActions.Add(new ConfigureContainerAdapter<TContainerBuilder>(configureDelegate
                    ?? throw new ArgumentNullException(nameof(configureDelegate))));
                return this;
            }
    
            /// <summary>
            /// Run the given actions to initialize the host. This can only be called once.
            /// </summary>
            /// <returns>An initialized <see cref="IHost"/></returns>
            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>();
            }
    
            private void BuildHostConfiguration()
            {
                var configBuilder = new ConfigurationBuilder()
                    .AddInMemoryCollection(); // Make sure there's some default storage since there are no default providers
    
                foreach (var buildAction in _configureHostConfigActions)
                {
                    buildAction(configBuilder);
                }
                _hostConfiguration = configBuilder.Build();
            }
    
            private void CreateHostingEnvironment()
            {
                _hostingEnvironment = new HostingEnvironment()
                {
                    ApplicationName = _hostConfiguration[HostDefaults.ApplicationKey],
                    EnvironmentName = _hostConfiguration[HostDefaults.EnvironmentKey] ?? Environments.Production,
                    ContentRootPath = ResolveContentRootPath(_hostConfiguration[HostDefaults.ContentRootKey], AppContext.BaseDirectory),
                };
    
                if (string.IsNullOrEmpty(_hostingEnvironment.ApplicationName))
                {
                    // Note GetEntryAssembly returns null for the net4x console test runner.
                    _hostingEnvironment.ApplicationName = Assembly.GetEntryAssembly()?.GetName().Name;
                }
    
                _hostingEnvironment.ContentRootFileProvider = new PhysicalFileProvider(_hostingEnvironment.ContentRootPath);
            }
    
            private string ResolveContentRootPath(string contentRootPath, string basePath)
            {
                if (string.IsNullOrEmpty(contentRootPath))
                {
                    return basePath;
                }
                if (Path.IsPathRooted(contentRootPath))
                {
                    return contentRootPath;
                }
                return Path.Combine(Path.GetFullPath(basePath), contentRootPath);
            }
    
            private void CreateHostBuilderContext()
            {
                _hostBuilderContext = new HostBuilderContext(Properties)
                {
                    HostingEnvironment = _hostingEnvironment,
                    Configuration = _hostConfiguration
                };
            }
    
            private void BuildAppConfiguration()
            {
                var configBuilder = new ConfigurationBuilder()
                    .SetBasePath(_hostingEnvironment.ContentRootPath)
                    .AddConfiguration(_hostConfiguration, shouldDisposeConfiguration: true);
    
                foreach (var buildAction in _configureAppConfigActions)
                {
                    buildAction(_hostBuilderContext, configBuilder);
                }
                _appConfiguration = configBuilder.Build();
                _hostBuilderContext.Configuration = _appConfiguration;
            }
    
            private void CreateServiceProvider()
            {
                var services = new ServiceCollection();
    #pragma warning disable CS0618 // Type or member is obsolete
                services.AddSingleton<IHostingEnvironment>(_hostingEnvironment);
    #pragma warning restore CS0618 // Type or member is obsolete
                services.AddSingleton<IHostEnvironment>(_hostingEnvironment);
                services.AddSingleton(_hostBuilderContext);
                // register configuration as factory to make it dispose with the service provider
                services.AddSingleton(_ => _appConfiguration);
    #pragma warning disable CS0618 // Type or member is obsolete
                services.AddSingleton<IApplicationLifetime>(s => (IApplicationLifetime)s.GetService<IHostApplicationLifetime>());
    #pragma warning restore CS0618 // Type or member is obsolete
                services.AddSingleton<IHostApplicationLifetime, ApplicationLifetime>();
                services.AddSingleton<IHostLifetime, ConsoleLifetime>();
                services.AddSingleton<IHost, Internal.Host>();
                services.AddOptions();
                services.AddLogging();
    
                foreach (var configureServicesAction in _configureServicesActions)
                {
                    configureServicesAction(_hostBuilderContext, services);
                }
    
                var containerBuilder = _serviceProviderFactory.CreateBuilder(services);
    
                foreach (var containerAction in _configureContainerActions)
                {
                    containerAction.ConfigureContainer(_hostBuilderContext, containerBuilder);
                }
    
                _appServices = _serviceProviderFactory.CreateServiceProvider(containerBuilder);
    
                if (_appServices == null)
                {
                    throw new InvalidOperationException($"The IServiceProviderFactory returned a null IServiceProvider.");
                }
    
                // resolve configuration explicitly once to mark it as resolved within the
                // service provider, ensuring it will be properly disposed with the provider
                _ = _appServices.GetService<IConfiguration>();
            }
        }

    HostBuilder中的几个重要字段

    _configureHostConfigActions:主机配置的委托列表

    _configureAppConfigActions:应用配置的委托列表

    _configureServicesActions:服务注册的委托列表

    _configureContainerActions:配置依赖注入容器

    _serviceProviderFactory:服务提供工厂(默认实现new ServiceFactoryAdapter<IServiceCollection>(new DefaultServiceProviderFactory());)

    方法ConfigureHostConfiguration和ConfigureAppConfiguration分别向_configureHostConfigActions和_configureAppConfigActions列表中添加配置的委托

    方法ConfigureServices向_configureServicesActions添加服务

    方法UseServiceProviderFactory重新定义_serviceProviderFactory

    方法ConfigureContainer向_configureContainerActions添加容器的配置

    再看下HostBuilder的Build()方法是如何重建一个IHost出来的

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

    BuildHostConfiguration方法的实现

    private void BuildHostConfiguration()
            {
                var configBuilder = new ConfigurationBuilder()
                    .AddInMemoryCollection(); // Make sure there's some default storage since there are no default providers
    
                foreach (var buildAction in _configureHostConfigActions)
                {
                    buildAction(configBuilder);
                }
                _hostConfiguration = configBuilder.Build();
            }

    创建ConfigurationBuilder,并调用_configureHostConfigActions列表进行配置的初始化,构建IConfiguration赋值给字段_hostConfiguration

    CreateHostingEnvironment方法实现

    private void CreateHostingEnvironment()
            {
                _hostingEnvironment = new HostingEnvironment()
                {
                    ApplicationName = _hostConfiguration[HostDefaults.ApplicationKey],
                    EnvironmentName = _hostConfiguration[HostDefaults.EnvironmentKey] ?? Environments.Production,
                    ContentRootPath = ResolveContentRootPath(_hostConfiguration[HostDefaults.ContentRootKey], AppContext.BaseDirectory),
                };
    
                if (string.IsNullOrEmpty(_hostingEnvironment.ApplicationName))
                {
                    // Note GetEntryAssembly returns null for the net4x console test runner.
                    _hostingEnvironment.ApplicationName = Assembly.GetEntryAssembly()?.GetName().Name;
                }
    
                _hostingEnvironment.ContentRootFileProvider = new PhysicalFileProvider(_hostingEnvironment.ContentRootPath);
            }

    构建HostingEnvironment对象,从配置中获取key为applicationName、environment的应用名称和环境名称,key为contentRoot应用路径

    CreateHostBuilderContext方法实现

    private void CreateHostBuilderContext()
            {
                _hostBuilderContext = new HostBuilderContext(Properties)
                {
                    HostingEnvironment = _hostingEnvironment,
                    Configuration = _hostConfiguration
                };
            }

    HostBuilderContext宿主上下文,保存HostingEnvironment、Configuration和宿主的一些自定义属性

    BuildAppConfiguration的方法实现

    private void BuildAppConfiguration()
            {
                var configBuilder = new ConfigurationBuilder()
                    .SetBasePath(_hostingEnvironment.ContentRootPath)
                    .AddConfiguration(_hostConfiguration, shouldDisposeConfiguration: true);
    
                foreach (var buildAction in _configureAppConfigActions)
                {
                    buildAction(_hostBuilderContext, configBuilder);
                }
                _appConfiguration = configBuilder.Build();
                _hostBuilderContext.Configuration = _appConfiguration;
            }

    重新构建ConfigurationBuilder,并合并之前的_hostConfiguration,调用_configureAppConfigActions列表初始化应用配置

    CreateServiceProvider方法的实现

    private void CreateServiceProvider()
            {
                var services = new ServiceCollection();
    #pragma warning disable CS0618 // Type or member is obsolete
                services.AddSingleton<IHostingEnvironment>(_hostingEnvironment);
    #pragma warning restore CS0618 // Type or member is obsolete
                services.AddSingleton<IHostEnvironment>(_hostingEnvironment);
                services.AddSingleton(_hostBuilderContext);
                // register configuration as factory to make it dispose with the service provider
                services.AddSingleton(_ => _appConfiguration);
    #pragma warning disable CS0618 // Type or member is obsolete
                services.AddSingleton<IApplicationLifetime>(s => (IApplicationLifetime)s.GetService<IHostApplicationLifetime>());
    #pragma warning restore CS0618 // Type or member is obsolete
                services.AddSingleton<IHostApplicationLifetime, ApplicationLifetime>();
                services.AddSingleton<IHostLifetime, ConsoleLifetime>();
                services.AddSingleton<IHost, Internal.Host>();
                services.AddOptions();
                services.AddLogging();
    
                foreach (var configureServicesAction in _configureServicesActions)
                {
                    configureServicesAction(_hostBuilderContext, services);
                }
    
                var containerBuilder = _serviceProviderFactory.CreateBuilder(services);
    
                foreach (var containerAction in _configureContainerActions)
                {
                    containerAction.ConfigureContainer(_hostBuilderContext, containerBuilder);
                }
    
                _appServices = _serviceProviderFactory.CreateServiceProvider(containerBuilder);
    
                if (_appServices == null)
                {
                    throw new InvalidOperationException($"The IServiceProviderFactory returned a null IServiceProvider.");
                }
    
                // resolve configuration explicitly once to mark it as resolved within the
                // service provider, ensuring it will be properly disposed with the provider
                _ = _appServices.GetService<IConfiguration>();
            }

    方法的第一行创建了一个服务容器,并添加IHostingEnvironment、IHostEnvironment、HostBuilderContext、IConfiguration、IHostApplicationLifetime、IHostLifetime等服务

    最后注册IHost,IHost的默认实现是一个内部类Host,下一章再介绍

    调用_configureServicesActions列表把其他服务注册进来

    看下IServiceFactoryAdapter和IServiceProviderFactory<TContainerBuilder>

    internal interface IServiceFactoryAdapter
        {
            object CreateBuilder(IServiceCollection services);
    
            IServiceProvider CreateServiceProvider(object containerBuilder);
        }
    public interface IServiceProviderFactory<TContainerBuilder>
        {
            /// <summary>
            /// Creates a container builder from an <see cref="IServiceCollection"/>.
            /// </summary>
            /// <param name="services">The collection of services</param>
            /// <returns>A container builder that can be used to create an <see cref="IServiceProvider"/>.</returns>
            TContainerBuilder CreateBuilder(IServiceCollection services);
    
            /// <summary>
            /// Creates an <see cref="IServiceProvider"/> from the container builder.
            /// </summary>
            /// <param name="containerBuilder">The container builder</param>
            /// <returns>An <see cref="IServiceProvider"/></returns>
            IServiceProvider CreateServiceProvider(TContainerBuilder containerBuilder);
        }

    CreateBuilder方法创建一个依赖注入容器,CreateServiceProvider方法根据CreateBuilder创建IServiceProvider

    IServiceFactoryAdapter的默认实现

    internal class ServiceFactoryAdapter<TContainerBuilder> : IServiceFactoryAdapter
        {
            private IServiceProviderFactory<TContainerBuilder> _serviceProviderFactory;
            private readonly Func<HostBuilderContext> _contextResolver;
            private Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> _factoryResolver;
    
            public ServiceFactoryAdapter(IServiceProviderFactory<TContainerBuilder> serviceProviderFactory)
            {
                _serviceProviderFactory = serviceProviderFactory ?? throw new ArgumentNullException(nameof(serviceProviderFactory));
            }
    
            public ServiceFactoryAdapter(Func<HostBuilderContext> contextResolver, Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factoryResolver)
            {
                _contextResolver = contextResolver ?? throw new ArgumentNullException(nameof(contextResolver));
                _factoryResolver = factoryResolver ?? throw new ArgumentNullException(nameof(factoryResolver));
            }
    
            public object CreateBuilder(IServiceCollection services)
            {
                if (_serviceProviderFactory == null)
                {
                    _serviceProviderFactory = _factoryResolver(_contextResolver());
    
                    if (_serviceProviderFactory == null)
                    {
                        throw new InvalidOperationException("The resolver returned a null IServiceProviderFactory");
                    }
                }
                return _serviceProviderFactory.CreateBuilder(services);
            }
    
            public IServiceProvider CreateServiceProvider(object containerBuilder)
            {
                if (_serviceProviderFactory == null)
                {
                    throw new InvalidOperationException("CreateBuilder must be called before CreateServiceProvider");
                }
    
                return _serviceProviderFactory.CreateServiceProvider((TContainerBuilder)containerBuilder);
            }
        }

    IServiceProviderFactory<TContainerBuilder>的默认实现

    /// <summary>
        /// Default implementation of <see cref="IServiceProviderFactory{TContainerBuilder}"/>.
        /// </summary>
        public class DefaultServiceProviderFactory : IServiceProviderFactory<IServiceCollection>
        {
            private readonly ServiceProviderOptions _options;
    
            /// <summary>
            /// Initializes a new instance of the <see cref="DefaultServiceProviderFactory"/> class
            /// with default options.
            /// </summary>
            /// <seealso cref="ServiceProviderOptions.Default"/>
            public DefaultServiceProviderFactory() : this(ServiceProviderOptions.Default)
            {
    
            }
    
            /// <summary>
            /// Initializes a new instance of the <see cref="DefaultServiceProviderFactory"/> class
            /// with the specified <paramref name="options"/>.
            /// </summary>
            /// <param name="options">The options to use for this instance.</param>
            public DefaultServiceProviderFactory(ServiceProviderOptions options)
            {
                if (options == null)
                {
                    throw new ArgumentNullException(nameof(options));
                }
    
                _options = options;
            }
    
            /// <inheritdoc />
            public IServiceCollection CreateBuilder(IServiceCollection services)
            {
                return services;
            }
    
            /// <inheritdoc />
            public IServiceProvider CreateServiceProvider(IServiceCollection containerBuilder)
            {
                return containerBuilder.BuildServiceProvider(_options);
            }
        }

    最后看下Build()方法的返回值

    return _appServices.GetRequiredService<IHost>();

    可以看到是在服务容器中解析出IHost出来的

    以上就是IHostBuilder创建IHost的过程,下篇再看下IHost的具体实现

  • 相关阅读:
    人物-IT-张志东:张志东
    人物-IT-任正非:任正非
    人物-IT-张朝阳:张朝阳
    院校-清华大学:清华大学
    人物-丁磊:丁磊
    人物-李彦宏:李彦宏
    人物-IT-马云:马云
    inittab
    initlocation
    initdb
  • 原文地址:https://www.cnblogs.com/lanpingwang/p/12550676.html
Copyright © 2020-2023  润新知