------------------------------------------------------------------------------------------------------------
寫在前面:這是一個系列的文章,總目錄請移步:NetCore2.0技術文章目錄
------------------------------------------------------------------------------------------------------------
using Microsoft.AspNetCore.Hosting; namespace MyWeb { class Program { static void Main(string[] args) { var host = new WebHostBuilder() .UseKestrel() // 指定WebServer為Kestrel .UseStartup<StartUpB>() // 配置WebHost .Build(); host.Run(); // 啟動WebHost } } }
框架接入的關鍵代碼是WebHostBuilder.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<IHostingEnvironment>(); return new ConventionBasedStartup(StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName)); }); } }); }
首先這是IWebHostBuilder接口的擴展類,這里有兩個分支
1、如果StartUp從IStartup繼承,則直接以單例的方式加入插件服務框架中。
2、如果不是從IStartup繼承,則包裝為IStartup后,再以單例的方式加入插件服務框架中。
源碼證實了ConventionBasedStartup類正是繼承了IStartup。

public class ConventionBasedStartup : IStartup { private readonly StartupMethods _methods; public ConventionBasedStartup(StartupMethods methods) { _methods = methods; } public void Configure(IApplicationBuilder app) { try { _methods.ConfigureDelegate(app); } catch (Exception ex) { if (ex is TargetInvocationException) { ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); } throw; } } public IServiceProvider ConfigureServices(IServiceCollection services) { try { return _methods.ConfigureServicesDelegate(services); } catch (Exception ex) { if (ex is TargetInvocationException) { ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); } throw; } } }
二、框架如何包裝我們的StartUp類
從源碼看出關鍵代碼是StartupLoader.LoadMethods,我們看看框架源碼(省略了部分代碼)
public static StartupMethods LoadMethods(IServiceProvider hostingServiceProvider, Type startupType, string environmentName) { var configureMethod = FindConfigureDelegate(startupType, environmentName); var servicesMethod = FindConfigureServicesDelegate(startupType, environmentName); object instance = null; if (!configureMethod.MethodInfo.IsStatic || (servicesMethod != null && !servicesMethod.MethodInfo.IsStatic)) { instance = ActivatorUtilities.GetServiceOrCreateInstance(hostingServiceProvider, startupType); } Func<IServiceCollection, IServiceProvider> configureServices = services => { return services.BuildServiceProvider(); }; return new StartupMethods(instance, configureMethod.Build(instance), configureServices); }
我們猜測FindConfigureDelegate方法接入了我們的StartUp,源碼證實了,框架通過反射拿到了我們的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繼承自IStartup
從上面分析可以看出,框架可以接入兩種StartUp,
- 一種是繼承自IStartup的類
- 另外一種是包含Configure方法的類
既然如此,我們的StartUp可不可以直接繼承自IStartup呢?實驗證明是可以的,代碼如下:

using System; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; namespace MyWeb { class StartUpI : IStartup { public void Configure(IApplicationBuilder app) { app.Run(c => { var req = c.Request.Path.ToString().TrimStart('/'); var res = string.Empty; switch (req) { case "1": res = "one"; break; case "2": res = "two"; break; default: res = "none"; break; } var mtd = string.Empty; switch (c.Request.Method) { case "GET": mtd = "請求方式: get"; break; case "POST": mtd = "請求方式:post"; break; default: mtd = "請求方式:none"; break; } return c.Response.WriteAsync(res); }); } public IServiceProvider ConfigureServices(IServiceCollection services) { return services.BuildServiceProvider(); } } }
我們把這個類傳給框架
using Microsoft.AspNetCore.Hosting; namespace MyWeb { class Program { static void Main(string[] args) { var host = new WebHostBuilder() .UseKestrel() // 指定WebServer為Kestrel .UseStartup<StartUpI>() // 配置WebHost .Build(); host.Run(); // 啟動WebHost } } }
然后運行程序,結果正如期待的:OK!
四、框架實現了一個繼承自IStartup的抽象基類
通過查找IStartup的引用關系,發現框架實現了一個抽象基類StartupBase:
public abstract class StartupBase : IStartup { public abstract void Configure(IApplicationBuilder app); IServiceProvider IStartup.ConfigureServices(IServiceCollection services) { ConfigureServices(services); return CreateServiceProvider(services); } public virtual void ConfigureServices(IServiceCollection services) { } public virtual IServiceProvider CreateServiceProvider(IServiceCollection services) { return services.BuildServiceProvider(); } }
我們看到,只需要實現Configure這個抽象方法就可以完成StartUp的定制了,減少了我們的開發工作量,我們來實現一個子類:

using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; namespace MyWeb { class StartUpB : StartupBase { public override void Configure(IApplicationBuilder app) { app.Run(c => { var req = c.Request.Path.ToString().TrimStart('/'); var res = string.Empty; switch (req) { case "1": res = "one"; break; case "2": res = "two"; break; default: res = "none"; break; } var mtd = string.Empty; switch (c.Request.Method) { case "GET": mtd = "請求方式: get"; break; case "POST": mtd = "請求方式:post"; break; default: mtd = "請求方式:none"; break; } return c.Response.WriteAsync(res); }); } } }
我們把這個類傳給框架
using Microsoft.AspNetCore.Hosting; namespace MyWeb { class Program { static void Main(string[] args) { var host = new WebHostBuilder() .UseKestrel() // 指定WebServer為Kestrel .UseStartup<StartUpB>() // 配置WebHost .Build(); host.Run(); // 啟動WebHost } } }
然后運行程序,結果正如期待的:OK!
五、性能分析
至此我們弄清楚了ASP.Net Core2.0如何接入StartUp類,有三種方式:
1、自己定義個類,必須包含Configure方法
2、繼承自IStartup,實現所有方法
3、繼承自StartupBase抽象類,只需要實現Configure方法
我認為第三種方式相對來講,效率更高,原因有二:
1、只需要實現一個方法,代碼最少
2、不需要反射,效率更高
不知道,微軟新建ASP.Net Core2.0 工程,默認使用了第一種方式,是從哪個角度考慮的???