深入探究ASP.NET Core Startup初始化问题
如果你在ASP.NET Core 3.1中使用过Autofac那么你对ConfigureContainer方法一定不陌生,它和ConfigureServices、Configure方法一样的神奇,在几乎没有任何约束的情况下我们只需要定义ConfigureContainer方法并为方法传递一个ContainerBuilder参数,那么这个方法就能顺利的被调用了。这一切究竟是如何实现的呢,接下来我们继续探究源码,找到了如下的逻辑 //根据规则查找最终返回ConfigureContainerBuilder实例 var configureContainerBuilder = StartupLoader.FindConfigureContainerDelegate(startupType, context.HostingEnvironment.EnvironmentName); if (configureContainerBuilder.MethodInfo != null) { //获取容器类型比如如果是autofac则类型为ContainerBuilder var containerType = configureContainerBuilder.GetContainerType(); // 存储configureContainerBuilder实例 _builder.Properties[typeof(ConfigureContainerBuilder)] = configureContainerBuilder; //构建一个Action<HostBuilderContext,containerType>类型的委托 var actionType = typeof(Action<,>).MakeGenericType(typeof(HostBuilderContext), containerType); // 获取此类型的私有ConfigureContainer方法,然后声明该方法的泛型为容器类型,然后创建这个方法的委托 var configureCallback = GetType().GetMethod(nameof(ConfigureContainer), BindingFlags.NonPublic | BindingFlags.Instance) .MakeGenericMethod(containerType) .CreateDelegate(actionType, this); // 等同于执行_builder.ConfigureContainer<T>(ConfigureContainer),其中T为容器类型。 //C onfigureContainer表示一个委托,即我们在Startup中定义的ConfigureContainer委托 typeof(IHostBuilder).GetMethods().First(m => m.Name == nameof(IHostBuilder.ConfigureContainer)) .MakeGenericMethod(containerType) .InvokeWithoutWrappingExceptions(_builder, new object[] { configureCallback }); } 继续使用老配方,我们查看StartupLoader的FindConfigureContainerDelegate方法实现 internal static ConfigureContainerBuilder FindConfigureContainerDelegate(Type startupType, string environmentName) { //根据startupType和根据environmentName构建的Configure{0}Services字符串先去查找返回类型为IServiceProvider的方法 var configureMethod = FindMethod(startupType, "Configure{0}Container", environmentName, typeof(void), required: false); //用查找到的方法去初始化ConfigureContainerBuilder return new ConfigureContainerBuilder(configureMethod); } 果然还是这个配方这个味道,废话不多说直接查看ConfigureContainerBuilder源码 internal class ConfigureContainerBuilder { public ConfigureContainerBuilder(MethodInfo configureContainerMethod) { MethodInfo = configureContainerMethod; } public MethodInfo MethodInfo { get; } public Func<Action<object>, Action<object>> ConfigureContainerFilters { get; set; } = f => f; public Action<object> Build(object instance) => container => Invoke(instance, container); //查找容器类型,其实就是ConfigureContainer方法的的唯一参数 public Type GetContainerType() { var parameters = MethodInfo.GetParameters(); //ConfigureContainer方法只能包含一个参数 if (parameters.Length != 1) { throw new InvalidOperationException($"The {MethodInfo.Name} method must take only one parameter."); } return parameters[0].ParameterType; } private void Invoke(object instance, object container) { ConfigureContainerFilters(StartupConfigureContainer)(container); void StartupConfigureContainer(object containerBuilder) => InvokeCore(instance, containerBuilder); } //根据传递的container对象执行ConfigureContainer方法逻辑比如使用autofac时ConfigureContainer(ContainerBuilder) private void InvokeCore(object instance, object container) { if (MethodInfo == null) { return; } var arguments = new object[1] { container }; MethodInfo.InvokeWithoutWrappingExceptions(instance, arguments); } } 果不其然千年老方下来还是那个味道,和ConfigureServices、Configure方法思路几乎一致。这里需要注意的是GetContainerType获取的容器类型是ConfigureContainer方法的唯一参数即容器类型,如果传递多个参数则直接抛出异常。其实Startup的ConfigureContainer方法经过花里胡哨的一番操作之后,最终还是转换成了雷士如下的操作方式,这个我们在上面代码中构建actionType的时候就可以看出,最终通过查找到的容器类型去完成注册等相关操作,这里就不过多的讲解了 Host.CreateDefaultBuilder(args) .ConfigureContainer<ContainerBuilder>((context,container)=> { container.RegisterType<PersonService>().As<IPersonService>().InstancePerLifetimeScope(); }); 总结 (编辑:焦作站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |