Asp.net core 中的Startup类是啥?

前言碎语

在上一篇中, 我写了一个小的实例来运行简单的web 服务. 用心的看官肯定发现了, 在我的项目中有一个Startup类, 此类并没有继承任何接口, 只是实现了一个Configure方法, 这个Configure方法则是主要的逻辑所在了. 那么, 这个Startup类是如何被Web 服务调用的, 怎能在没有实现任何接口的情况下被调用呢?

Asp.net core 中的Startup类是啥?_第1张图片

深入调研

dotnet core 自诞生以来便为dotnet 社区带来了跨平台和开源, 两个当今互联网技术不可或缺的属性. 要说这几年来, 由于dotnet跨平台的不利, dotnet社区已经日渐式微. 2016年发布的dotnet core可谓直指要害, 微软也希望借开源社区之力把dotnet 平台推向一个新的高峰.
既然是开源, 我们就有机会获取到更多有用的信息, 帮助我们更加深刻的理解dotnet 平台. 带着上面的问题, 我们来看看源码. 源码地址, 有兴趣的童鞋自行查阅.
打开源码后很容易发现, 原来在Microsoft.AspNetCore.Hosting有一个StartupBase基类, 并且实现了IStartup接口. 感觉直接继承基类, 实现一个Startup类也是可以的. 童鞋们敬请尝试.

再来看下demo中的代码:

    var host = new WebHostBuilder()
        .UseKestrel()
        .UseStartup()
        .Build();

    host.Run();

涉及到Startup类的方法是UseStartup这个函数. 打开源码可以透过重载找到如下实现:

    public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType)
    {
        var startupAssemblyName = startupType.GetTypeInfo().Assembly.GetName().Name;

        return hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName)
                          .ConfigureServices(services =>
                          {
                              if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo()))
                              {
                                  services.AddSingleton(typeof(IStartup), startupType);
                              }
                              else
                              {
                                  services.AddSingleton(typeof(IStartup), sp =>
                                  {
                                      var hostingEnvironment = sp.GetRequiredService();
                                      return new ConventionBasedStartup(StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName));
                                  });
                              }
                          });
    }

果不其然, 源码中也对是否继承IStartup接口进行了检查, 并分别有两个不同的逻辑分支. 我们暂且看没有实现接口的一支.

直观的看代码, 意图很明显, 要把Startup类中的方法作为具体逻辑, 加入到一个单例的服务中. 具体的实现逻辑应该在这一句:

return new ConventionBasedStartup(StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName));

看下ConventionBasedStartup类的定义:

public class ConventionBasedStartup : IStartup

看到这样的接口继承, 我突然感慨到, 自己是多么的睿智. Hosting类库自动将我们的非接口继承的Startup类转换成继承接口的形式, 不可谓不蛋疼.

Asp.net core 中的Startup类是啥?_第2张图片

洞察天机

接着我找到了主要的代码逻辑, 先来看一下是如何找到Startup类中的Configure方法的:

    private static ConfigureBuilder FindConfigureDelegate(Type startupType, string environmentName)
    {
        var configureMethod = FindMethod(startupType, "Configure{0}", environmentName, typeof(void), required: true);
        return new ConfigureBuilder(configureMethod);
    }

就是这样的一段代码, 没错, 竟然是字符串匹配, 换句话说, 你必须要在Startup中实现Configure方法, 不然...

Asp.net core 中的Startup类是啥?_第3张图片

接着看这段:

    public static StartupMethods LoadMethods(IServiceProvider hostingServiceProvider, Type startupType, string environmentName)
    {
        var configureMethod = FindConfigureDelegate(startupType, environmentName);
        var servicesMethod = FindConfigureServicesDelegate(startupType, environmentName);
        var configureContainerMethod = FindConfigureContainerDelegate(startupType, environmentName);

        object instance = null;
        if (!configureMethod.MethodInfo.IsStatic || (servicesMethod != null && !servicesMethod.MethodInfo.IsStatic))
        {
            instance = ActivatorUtilities.GetServiceOrCreateInstance(hostingServiceProvider, startupType);
        }

        var configureServicesCallback = servicesMethod.Build(instance);
        var configureContainerCallback = configureContainerMethod.Build(instance);

        Func configureServices = services =>
        {
            // Call ConfigureServices, if that returned an IServiceProvider, we're done
            IServiceProvider applicationServiceProvider = configureServicesCallback.Invoke(services);

            if (applicationServiceProvider != null)
            {
                return applicationServiceProvider;
            }

            // If there's a ConfigureContainer method
            if (configureContainerMethod.MethodInfo != null)
            {
                // We have a ConfigureContainer method, get the IServiceProviderFactory
                var serviceProviderFactoryType = typeof(IServiceProviderFactory<>).MakeGenericType(configureContainerMethod.GetContainerType());
                var serviceProviderFactory = hostingServiceProvider.GetRequiredService(serviceProviderFactoryType);
                // var builder = serviceProviderFactory.CreateBuilder(services);
                var builder = serviceProviderFactoryType.GetMethod(nameof(DefaultServiceProviderFactory.CreateBuilder)).Invoke(serviceProviderFactory, new object[] { services });
                configureContainerCallback.Invoke(builder);
                // applicationServiceProvider = serviceProviderFactory.CreateServiceProvider(builder);
                applicationServiceProvider = (IServiceProvider)serviceProviderFactoryType.GetMethod(nameof(DefaultServiceProviderFactory.CreateServiceProvider)).Invoke(serviceProviderFactory, new object[] { builder });
            }
            else
            {
                // Get the default factory
                var serviceProviderFactory = hostingServiceProvider.GetRequiredService>();

                // Don't bother calling CreateBuilder since it just returns the default service collection
                applicationServiceProvider = serviceProviderFactory.CreateServiceProvider(services);
            }

            return applicationServiceProvider ?? services.BuildServiceProvider();
        };

        return new StartupMethods(configureMethod.Build(instance), configureServices);
    }

微软为了方便开发人员, 竟然大费周章用到反射处理一个Startup. 不禁感叹, 微软的攻城狮们, 你们是有多闲?

如果您觉得这篇文章对您有那么一丁点益处, 或者从某个角度触动到了您, 请给川酷一些鼓励, 打赏, 点赞, 关注, 哪怕评论区骂我两句, 鄙人都感激涕零.

你可能感兴趣的:(Asp.net core 中的Startup类是啥?)