深入探究ASP.NET Core Startup初始化问题(4)

internal class ConfigureServicesBuilder { //构造函数传递的configureServices的MethodInfo public ConfigureServicesBuilder(MethodInfo configureServices) { MethodInfo = configureServices; } public MethodInfo MethodInfo { get; } public Func<Func<IServiceCollection, IServiceProvider>, Func<IServiceCollection, IServiceProvider>> StartupServiceFilters { get; set; } = f => f; //Build委托 public Func<IServiceCollection, IServiceProvider> Build(object instance) => services => Invoke(instance, services); private IServiceProvider Invoke(object instance, IServiceCollection services) { //执行StartupServiceFilters委托参数为Func<IServiceCollection, IServiceProvider>类型的委托方法即Startup //返回了Func<IServiceCollection, IServiceProvider>委托,执行这个委托需传递services即IServiceCollections实例返回IServiceProvider类型 return StartupServiceFilters(Startup)(services); IServiceProvider Startup(IServiceCollection serviceCollection) => InvokeCore(instance, serviceCollection); } private IServiceProvider InvokeCore(object instance, IServiceCollection services) { if (MethodInfo == null) { return null; } // 如果ConfigureServices方法包含多个参数或方法参数类型不是IServiceCollection类型则直接抛出异常 // 也就是说ConfigureServices只能包含一个参数且类型为IServiceCollection var parameters = MethodInfo.GetParameters(); if (parameters.Length > 1 || parameters.Any(p => p.ParameterType != typeof(IServiceCollection))) { throw new InvalidOperationException("The ConfigureServices method must either be parameterless or take only one parameter of type IServiceCollection."); } //找到ConfigureServices方法的参数,并将services即IServiceCollection的实例传递给这个参数 var arguments = new object[MethodInfo.GetParameters().Length]; if (parameters.Length > 0) { arguments[0] = services; } // 执行返回IServiceProvider实例 return MethodInfo.InvokeWithoutWrappingExceptions(instance, arguments) as IServiceProvider; } }

看完ConfigureServicesBuilder类的实现逻辑,关于通过什么样的逻辑查找并执行ConfigureServices方法的逻辑就非常清晰了。首先是查找ConfigureServices方法,即包含环境变量的ConfigureServices方法名称比如(ConfigureDevelopmentServices)或名为ConfigureServices的方法,返回的是ConfigureServicesBuilder对象。然后执行ConfigureServicesBuilder的Build方法,这个方法里包含了执行ConfigureServices的规则,即ConfigureServices只能包含一个参数且类型为IServiceCollection,然后将当前程序中存在的IServiceCollection实例传递给它。

Configure的装载

我们常使用Startup的Configure方法去配置中间件,默认生成的Configure方法为我们添加了IApplicationBuilder和IWebHostEnvironment实例,但是其实Configure方法不仅仅可以传递这两个参数,它可以通过参数注入在IServiceCollection中注册的所有服务,究竟是如何实现的呢,接下来我们继续探究UseStartup方法查找源码查看想实现
[点击查看源码👈],我们抽离出来核心实现如下

//和ConfigureServices查找方式类似传递Startup实例和环境变量 ConfigureBuilder configureBuilder = StartupLoader.FindConfigureDelegate(startupType, context.HostingEnvironment.EnvironmentName); services.Configure<GenericWebHostServiceOptions>(options => { //通过查看GenericWebHostServiceOptions的源码可知app其实就是IApplicationBuilder实例 options.ConfigureApplication = app => { startupError?.Throw(); //执行Startup.Configure,instance为Startup实例 if (instance != null && configureBuilder != null) { //执行Configure方法传递Startup实例和IApplicationBuilder实例 configureBuilder.Build(instance)(app); } }; });

我们通过查看GenericWebHostServiceOptions的源码可知ConfigureApplication属性的类型为Action也就是说app参数其实就是IApplicationBuilder接口的实例。通过上面这段代码可以看出,主要逻辑就是调用StartupLoader的FindConfigureDelegate方法,然后返回ConfigureBuilder建造类,然后构建出Configure方法并执行。首先我们来查看FindConfigureDelegate的逻辑实现
[点击查看源码👈]

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wswspw.html