通过上述代码我们可以看到在通过配置指定程序集时是如何查找指定规则的Startup类的,基本上可以理解为先去查找名称为Startup+环境变量的类,如果找不到则继续查找名称为Startup的类,最终会返回Startup的类型传递给UseStartup方法。其实我们最常使用的UseStartup()方法最终也是转换成UseStartup(typeof(T))的方式,所以最终这两种方式走到了相同的地方,接下来我们步入正题,来一起探究一下Starup究竟是如何被初始化的。
Startup的构造函数
相信对Startup有所了解的同学们都比较清楚,在使用泛型主机(IHostBuilder)时Startup的构造函数只支持注入IWebHostEnvironment、IHostEnvironment、IConfiguration,这个在微软官方文档中也有介绍,如果还有不熟悉这个操作的请先反思一下自己,然后在查阅微软官方文档。接下来我们就从源码着手,来探究一下它到底是如何做到的。沿着上述的操作,继续查看UseStartup里的代码找到了如下的实现[点击查看源码👈]
//创建Startup实例 object instance = ActivatorUtilities.CreateInstance(new HostServiceProvider(webHostBuilderContext), startupType);
这里的startupType就是我们传递的Startup类型,关于ActivatorUtilities这个类还是比较实用的,它为我们提供了许多帮助我们实例化对象的方法,在日常编程中如果有需要可以使用这个类。上面的ActivatorUtilities的CreateInstance方法的功能就是根据传递IServiceProvider类型的对象去实例化指定的类型对象,我们这里的类型就是startupType。它的使用场景就是,如果某个类型需要用过有参构造函数去实例化,而构造函数的参数可以来自于IServiceProvider的实例,那么使用这个方法就在合适不过了。上面的代码传递的IServiceProvider的实例是HostServiceProvider对象,接下来我们找到它的实现源码[点击查看源码👈]代码并不多我们就全部粘贴出来
private class HostServiceProvider : IServiceProvider { private readonly WebHostBuilderContext _context; public HostServiceProvider(WebHostBuilderContext context) { _context = context; } public object GetService(Type serviceType) { // 通过这里我们就比较清晰的看出,只有满足这几种情况下才能返回具体的实例,其他的都会返回null #pragma warning disable CS0618 // Type or member is obsolete if (serviceType == typeof(Microsoft.Extensions.Hosting.IHostingEnvironment) || serviceType == typeof(Microsoft.AspNetCore.Hosting.IHostingEnvironment) #pragma warning restore CS0618 // Type or member is obsolete || serviceType == typeof(IWebHostEnvironment) || serviceType == typeof(IHostEnvironment) ) { return _context.HostingEnvironment; } if (serviceType == typeof(IConfiguration)) { return _context.Configuration; } //不满足这几种情况的类型都返回null return null; } }
通过这个内部私有类我们就能清晰的看到为何Starup的构造函数只能注入IWebHostEnvironment、IHostEnvironment、IConfiguration相关实例了,HostServiceProvider类实现了IServiceProvider的GetService方法并做了判断,只有满足这几种类型才能返回具体的实例注入,其它不满足条件的类型都会返回null。因此在初始化Starup实例的时候,通过构造函数注入的类型也就只能是这几种了。最终通过这个构造函数初始化了Startup类的实例。
ConfigureServices的装载
接下来我们就来在UseStartup方法里继续查看是如何查找并执行ConfigureServices方法的,继续查看找到如下实现[点击查看源码👈]
//传递startupType和环境变量参数查找返回ConfigureServicesBuilder var configureServicesBuilder = StartupLoader.FindConfigureServicesDelegate(startupType, context.HostingEnvironment.EnvironmentName); //调用Build方法返回ConfigureServices委托 var configureServices = configureServicesBuilder.Build(instance); //传递services对象即IServiceCollection对象调用ConfigureServices方法 configureServices(services);