Asp.net Core启动流程讲解(四)
Asp.net Core内 DI(DependencyInjection)贯穿了项目的始终,要学习Asp.net Core就无法越过DI。
下面讲解一下DI在Asp.Net Core内的流程
asp.net core 3.0以下
Asp.Net core 3.0以下有两种自定义替换DI容器的方式
替换 IServiceProviderFactory 的默认实现,以及IStartup.Configure 函数修改返回值
1、IServiceProviderFactory
查看 WebHostBuilder.Build
        public IWebHost Build()
        {
            var hostingServices = BuildCommonServices(out var hostingStartupErrors);
            var applicationServices = hostingServices.Clone();
            var hostingServiceProvider = GetProviderFromFactory(hostingServices);
            AddApplicationServices(applicationServices, hostingServiceProvider);
            var host = new WebHost(
                applicationServices,
                hostingServiceProvider,
                _options,
                _config,
                hostingStartupErrors);
            try
            {
                host.Initialize();
                //省略不重要的代码段
                return host;
            }
            catch
            {
                host.Dispose();
                throw;
            }
            IServiceProvider GetProviderFromFactory(IServiceCollection collection)
            {
                var provider = collection.BuildServiceProvider();
                var factory = provider.GetService<IServiceProviderFactory<IServiceCollection>>();
                if (factory != null && !(factory is DefaultServiceProviderFactory))
                {
                    using (provider)
                    {
                        return factory.CreateServiceProvider(factory.CreateBuilder(collection));
                    }
                }
                return provider;
            }
        }
再跟进 BuildCommonServices 函数
        private IServiceCollection BuildCommonServices(out AggregateException hostingStartupErrors)
        {
            var services = new ServiceCollection();
            //省略不重要的代码段
            services.AddTransient<IServiceProviderFactory<IServiceCollection>, DefaultServiceProviderFactory>();
            _configureServices?.Invoke(_context, services);
            return services;
        }
思路很简,注册DI,然后返回 IServiceProvider 给 Host 容器,找到 IServiceProviderFactory ,判断类型不是
DefaultServiceProviderFactory,则转换DI,最后返回新的 IServiceProvider。
       public interface IServiceProviderFactory<TContainerBuilder>
       {
              TContainerBuilder CreateBuilder(IServiceCollection services);
              IServiceProvider CreateServiceProvider(TContainerBuilder
containerBuilder);
       }
WebHostBuilder.ConfigureServices 内DI注册 默认了实现的IServiceProviderFactory
IServiceProviderFactory 的流程是 IServiceProviderFactory.CreateBuilder->IServiceProviderFactory.CreateServiceProvider
先把DI容器 IServiceCollection 转换成自定义容器,再通过自定义容器转换成 IServiceProvider,最后依赖于根节点的IServiceProvider 即可
注册 IServiceProviderFactory 还有一处逻辑
WebHostBuilder.UseDefaultServiceProvider 转到源码
        public static IWebHostBuilder UseDefaultServiceProvider(this IWebHostBuilder hostBuilder, Action<WebHostBuilderContext, ServiceProviderOptions> configure)
        {
            return hostBuilder.ConfigureServices((context, services) =>
            {
                var options = new ServiceProviderOptions();
                configure(context, options);
                //替换默认的IServiceProviderFactory<IServiceCollection>
                services.Replace(ServiceDescriptor.Singleton<IServiceProviderFactory<IServiceCollection>>(new DefaultServiceProviderFactory(options)));
            });
        }
异曲同工之妙,不再赘述。
2、IStartup.Configure
还是打开熟悉的 WebHostBuilder.BuildCommonServices
        private IServiceCollection BuildCommonServices(out AggregateException hostingStartupErrors)
        {
            var services = new ServiceCollection();
            //忽略无关代码
            if (!string.IsNullOrEmpty(_options.StartupAssembly))
            {
                try
                {
                    var startupType = StartupLoader.FindStartupType(_options.StartupAssembly, _hostingEnvironment.EnvironmentName);
                    if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo()))
                    {
                        services.AddSingleton(typeof(IStartup), startupType);
                    }
                    else
                    {
                        services.AddSingleton(typeof(IStartup), sp =>
                        {
                            var hostingEnvironment = sp.GetRequiredService<IHostEnvironment>();
                            var methods = StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName);
                            return new ConventionBasedStartup(methods);
                        });
                    }
                }
                catch (Exception ex)
                {
                    var capture = ExceptionDispatchInfo.Capture(ex);
                    services.AddSingleton<IStartup>(_ =>
                    {
                        capture.Throw();
                        return null;
                    });
                }
            }
            _configureServices?.Invoke(_context, services);
            return services;
        }
注册 IStartup 到 DI
再查看 WebHostBuilder.Build
        public IWebHost Build()
        {
            var hostingServices = BuildCommonServices(out var hostingStartupErrors);
            var applicationServices = hostingServices.Clone();
            var hostingServiceProvider = GetProviderFromFactory(hostingServices);
            var host = new WebHost(
                applicationServices,
                hostingServiceProvider,
                _options,
                _config,
                hostingStartupErrors);
            try
            {
                host.Initialize();  //关建代码行
                //省略不重要的代码段
                return host;
            }
            catch
            {
                host.Dispose();
                throw;
            }
        }
查看 WebHost.Initialize 代码
        public void Initialize()
        {
            try
            {
                EnsureApplicationServices();
            }
            catch (Exception ex)
            {
                // EnsureApplicationServices may have failed due to a missing or throwing Startup class.
                if (_applicationServices == null)
                {
                    _applicationServices = _applicationServiceCollection.BuildServiceProvider();
                }
                if (!_options.CaptureStartupErrors)
                {
                    throw;
                }
                _applicationServicesException = ExceptionDispatchInfo.Capture(ex);
            }
        }
查看 WebHost.EnsureApplicationServices 代码
        private void EnsureApplicationServices()
        {
            if (_applicationServices == null)
            {
                EnsureStartup();
                _applicationServices = _startup.ConfigureServices(_applicationServiceCollection);
            }
        }
        private void EnsureStartup()
        {
            if (_startup != null)
            {
                return;
            }
            _startup = _hostingServiceProvider.GetService<IStartup>();
            if (_startup == null)
            {
                throw new InvalidOperationException($"No application configured. Please specify startup via IWebHostBuilder.UseStartup, IWebHostBuilder.Configure, injecting {nameof(IStartup)} or specifying the startup assembly via {nameof(WebHostDefaults.StartupAssemblyKey)} in the web host configuration.");
            }
        }
获取 IStartup 接口的实例,然后调用 IStartup.ConfigureServices 返回 IServiceProvider 实例
Asp.Net Core 3.0以上
Asp.Net Core 3.0以下的设计代码过于混乱,在Asp.Net Core 3.0开始,替换DI的流程变得简洁了
默认程序入口
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }
        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
查看 Host.CreateDefaultBuilder
        public static IHostBuilder CreateDefaultBuilder(string[] args)
        {
            var builder = new HostBuilder();
            //忽略无关代码
            return builder;
        }
转向 HostBuilder.Build
        public IHost Build()
        {
            //忽略无关代码
            CreateServiceProvider();
            return _appServices.GetRequiredService<IHost>();
        }
查看 HostBuilder.CreateServiceProvider
        private void CreateServiceProvider()
        {
            var services = new ServiceCollection();
            //忽略无关代码
            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>();
        }
这里有个 _serviceProviderFactory,转到定义
    public class HostBuilder : IHostBuilder
    {
        //忽略无关代码
        private IServiceFactoryAdapter _serviceProviderFactory = new ServiceFactoryAdapter<IServiceCollection>(new DefaultServiceProviderFactory());
    }
对应传参,有两个函数不同重载的UseServiceProviderFactory
        public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory)
        {
            _serviceProviderFactory = new ServiceFactoryAdapter<TContainerBuilder>(factory ?? throw new ArgumentNullException(nameof(factory)));
            return this;
        }
        public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory)
        {
            _serviceProviderFactory = new ServiceFactoryAdapter<TContainerBuilder>(() => _hostBuilderContext, factory ?? throw new ArgumentNullException(nameof(factory)));
            return this;
        }
后记
柠檬大佬说Asp.Net Core至少有三处可以替换DI的地方~ 还有一处
看了一下源码,大概是 IApplicationBuilderFactory 吧
    public interface IApplicationBuilderFactory
    {
        IApplicationBuilder CreateBuilder(IFeatureCollection serverFeatures);
    }
这里IApplicationBuilderFactory.CreateBuilder返回 IApplicationBuilder
IApplicationBuilder里的Ioc对象可以在这里设置
如果对于内容有交流和学习的,可以加 .Net应用程序框架交流群,群号386092459
Asp.net Core启动流程讲解(四)的更多相关文章
- ASP.NET Core启动流程
		1. 引言 对于ASP.NET Core应用程序来说,我们要记住非常重要的一点是:其本质上是一个独立的控制台应用,它并不是必需在IIS内部托管且并不需要IIS来启动运行(而这正是ASP.NET Cor ... 
- 一张图理清ASP.NET Core启动流程
		1. 引言 对于ASP.NET Core应用程序来说,我们要记住非常重要的一点是:其本质上是一个独立的控制台应用,它并不是必需在IIS内部托管且并不需要IIS来启动运行(而这正是ASP.NET Cor ... 
- Asp.net Core 启动流程分析
		新建的.net core 程序启动本质上是一个控制台应用程序,所以它的入口在Main方法中,所以启动的开始时从Main方法开始. public class Program { public stati ... 
- Asp.net core 启动流程
- 如何在ASP.NET Core中构造UrlHelper,及ASP.NET Core MVC路由讲解
		参考文章: Unable to utilize UrlHelper 除了上面参考文章中介绍的方法,其实在ASP.NET Core MVC的Filter拦截器中要使用UrlHelper非常简单.如下代码 ... 
- C#中的函数式编程:递归与纯函数(二)    学习ASP.NET Core Razor 编程系列四——Asp.Net Core Razor列表模板页面
		C#中的函数式编程:递归与纯函数(二) 在序言中,我们提到函数式编程的两大特征:无副作用.函数是第一公民.现在,我们先来深入第一个特征:无副作用. 无副作用是通过引用透明(Referential ... 
- 探索ASP.Net Core 3.0系列四:在ASP.NET Core 3.0的应用中启动时运行异步任务
		前言:在本文中,我将介绍ASP.NET Core 3.0 WebHost的微小更改如何使使用IHostedService在应用程序启动时更轻松地运行异步任务. 翻译 :Andrew Lock ht ... 
- asp.net core启动源码以及监听,到处理请求响应的过程
		摘要 asp.net core发布至今已经将近6年了,很多人对于这一块还是有些陌生,或者说没接触过:接触过的,对于asp.net core整个启动过程,监听过程,以及请求过程,响应过程也是一知半解,可 ... 
- Pro ASP.Net Core MVC 6th 第四章
		第四章 C# 关键特征 在本章中,我描述了Web应用程序开发中使用的C#特征,这些特征尚未被广泛理解或经常引起混淆. 这不是关于C#的书,但是,我仅为每个特征提供一个简单的例子,以便您可以按照本书其余 ... 
随机推荐
- Django学习路9_流程复习
			以下图示为 学习过程中,在千锋教育视频上截图的 视频链接: https://www.bilibili.com/video/BV1rx411X717?p=11 2 ... 
- PV与UV你的网站也可以
			个人博客网站分析 阅读前面的文章,有助于理解本文. 1.是时候来一个个人博客网站了 2.什么?你还没有自己的域名? 3.你的个人博客网站该上线了! 为什么需要流量分析? 各位小伙伴,请看下图,你们发现 ... 
- 好用的连接池-druid
			druid连接池是阿里巴巴的数据库连接池项目.它的一个亮点强大的监控功能以及防SQL注入,同时不影响性能.这里是它的GitHub地址.感觉druid扩展的功能还是很实用的. 实用的功能 详细的监控 E ... 
- 使用VMware虚拟机建立Ubuntu与主机win7的文件共享与传输
			1.要想在虚拟机与主机之间建立共享文件夹必须先安装VMware Tools.方法见https://www.cnblogs.com/lsc666js/p/13403919.html. 2.在VMware ... 
- MySQL-安装配置篇
			一.MySQL二进制安装包安装 1.环境初始化 1)创建目录mkdir /app/database --安装路径 mkdir /data/3306 --存放数据路径 mkdir /binlog/330 ... 
- CSS-flex|gird 布局
			网页布局.css *{ box-sizing: border-box; } /* flex弹性布局 */ #flex-container { display: flex; flex-direction ... 
- requests入门实践02_下载斗图拉最新表情包
			新版本移步:https://www.cnblogs.com/zy7y/p/13376228.html 下载斗图拉最新表情包 要爬取的目标所在网址:http://www.doutula.com/phot ... 
- JAVA学习:HashMap 和 ConcurrentHashMap
			一.最基本的HashMap 和 ConcurrentHashMap 1.HashMap的结构和底层原理:由数组和链表组成,数组里面每个地方都存了Key-Value这样的实例,在Java7叫Entry ... 
- 异步文件通道Java NIO你需要了解多少,来看看这篇文章
			在Java 7,AsynchronousFileChannel 被添加到了Java NIO中.使用AsynchronousFileChannel可以实现异步地读取和写入文件数据. 创建一个Asynch ... 
- 每日一道 LeetCode (8):删除排序数组中的重复项和移除元素
			每天 3 分钟,走上算法的逆袭之路. 前文合集 每日一道 LeetCode 前文合集 代码仓库 GitHub: https://github.com/meteor1993/LeetCode Gitee ... 
