04、NetCore2.0网页应用程序之Startup源码解析
来源:互联网 发布:灯杆数据基础调研 编辑:程序博客网 时间:2024/05/22 20:44
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 工程,默认使用了第一种方式,是从哪个角度考虑的???
- 04、NetCore2.0网页应用程序之Startup源码解析
- .netcore2.0+pgsql 脚手架
- .netcore2.0+pgsql 脚手架
- Android源码解析之应用程序启动进程
- netCore2.0 加载DonetTools失败
- WinCE6.0学习之EBoot源码分析----startup.s
- 网页病毒源码解析
- Android源码解析之应用程序启动过程源代码分析
- Android源码解析之应用程序发送广播过程分析
- 树莓派3中运行Netcore2.0程序
- Android应用程序入口源码解析
- WinCE6.0 Eboot StartUp.s解析
- Android应用程序安装过程解析(源码解析)
- Android应用程序安装过程解析(源码解析)
- 网页解析之Jsoup
- 粗略使用.NetCore2.0自带授权登陆Authorize
- 粗略使用.NetCore2.0自带授权登陆Authorize
- vs2017上.netCore2.0 EF的DataBase First使用笔记
- Hive分析函数和窗口函数
- Python-基础-时间日期处理小结
- Java 主线程和子线程的转换
- VFW音视频采集
- ViewPager相关
- 04、NetCore2.0网页应用程序之Startup源码解析
- 学习java多线程的笔记1--Thread(Runnable t)与重写run()方法等
- mysql千万级大数据SQL查询优化
- spring boot 输出简单 json格式的数据(利用分层结构)
- 2017年多校赛第九场 1005 FFF at Valentine(缩点+拓扑排序)
- ecmall 入口文件分析
- mongoose实现权限查询控制
- //请求数据, 省流量模式实现,下载安装,sdcard存储
- SSM(spring+springMVC+Mybatis)简单整合