04、NetCore2.0下Web应用之Startup源码解析
------------------------------------------------------------------------------------------------------------
写在前面:这是一个系列的文章,总目录请移步: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 "":
res = "one";
break;
case "":
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 "":
res = "one";
break;
case "":
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下Web应用之Startup源码解析的更多相关文章
- 03、NetCore2.0下Web应用之搭建最小框架
03.NetCore2.0下Web应用之搭建最小框架 这里我们不使用VS2017或者CLI命令的方式创建Asp.Net Core 2.0网页应用程序,而是完全手工的一点点搭建一个Web框架,以便更好的 ...
- Laravel框架下路由的使用(源码解析)
本篇文章给大家带来的内容是关于Laravel框架下路由的使用(源码解析),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 前言 我的解析文章并非深层次多领域的解析攻略.但是参考着开发文 ...
- abp vnext2.0核心组件之领域实体组件源码解析
接着abp vnext2.0核心组件之模块加载组件源码解析和abp vnext2.0核心组件之.Net Core默认DI组件切换到AutoFac源码解析集合.Net Core3.1,基本环境已经完备, ...
- NetCore2.0下使用EF CodeFirst创建数据库
本文所使用的VS版本:VS2017 15.3.0 首先新建一个.net core项目 取名NetCoreTask 使用模型视图控制器方式 新建Model层 在Model层下新建一个user实体类 1 ...
- Ubuntu 14.04 LTS 下 android 2.3.5 源码编译过程
Ubuntu 14.04 LTS 下 android 2.3.5 源码编译过程 在新的Ubuntu 64位系统下去编译早期的安卓源码是会出现很多问题的,因为64位系统在安装完成后,很多32位的兼容 ...
- Feign 系列(04)Contract 源码解析
Feign 系列(04)Contract 源码解析 [TOC] Spring Cloud 系列目录(https://www.cnblogs.com/binarylei/p/11563952.html# ...
- Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- 简单理解 OAuth 2.0 及资料收集,IdentityServer4 部分源码解析
简单理解 OAuth 2.0 及资料收集,IdentityServer4 部分源码解析 虽然经常用 OAuth 2.0,但是原理却不曾了解,印象里觉得很简单,请求跳来跳去,今天看完相关介绍,就来捋一捋 ...
- solr&lucene3.6.0源码解析(一)
本文作为系列的第一篇,主要描述的是solr3.6.0开发环境的搭建 首先我们需要从官方网站下载solr的相关文件,下载地址为http://archive.apache.org/dist/luc ...
随机推荐
- Java对象流的使用
为了让对象持久化(把对象存储到本地),可以使用java的对象流处理对象,把对象的内容写到本地存储的文件中,也可以从本地文件中读取出来.也就是常说的序列化和反序列化 主要用到了ObjectInputSt ...
- OpenCascade Law Function
OpenCascade Law Function eryar@163.com 1.Introduction 在OpenCASCADE的TKGeomAlgo Toolkit中提供了一个Law Packa ...
- Day3---------Linux操作系统目录结构
一.Linux系统文件树状结构 "/" 根目录 "." 当前目录 .. 父目录,既上一层目录 pwd 显示当前目录路径 ls. = ls = ls/ 显示当前目 ...
- 上传到 App Store 时出错。
Try this, it fixed it for me. Open Terminal and run: cd ~ mv .itmstransporter/ .old_itmstransporte ...
- [luogu1168]中位数_优先队列
中位数 题目大意:输出读入的前2*k+1个数的中位数.一共有n个数,按照读入顺序. 注释:$1\le n \le 10^9$. 想法:这是优先队列的一个应用qwq.我们弄两个堆.小根堆和大根堆,保证: ...
- C语言博客作业--数组
一.PTA实验作业 题目1.求整数序列中出现次数最多的数 1.本题PTA提交列表 2.设计思路 定义整形变量n,max,count分别表示整数个数,出现次数最大值,出现次数.定义循环变量i,j. 输入 ...
- MyGod_alpha版本测试报告
买尬-Alpha版本测试报告 @(二手市场APP)[MyGod团队|团队项目|版本测试] 项目名称:武汉大学校园二手市场APP--买尬 软件版本:1.0.0 开发团队:MyGod 开发代表:程环宇 张 ...
- 201621123050 《Java程序设计》第5周学习总结
1. 本周学习总结 1.1 写出你认为本周学习中比较重要的知识点关键词 答:接口.has-a.comparable 1.2 尝试使用思维导图将这些关键词组织起来.注:思维导图一般不需要出现过多的字. ...
- C语言——第十四、十五周作业
题目 题目一:交换最小值和最大值 1.实验代码 #include<stdio.h> int main() { ; int i , n; int a[N]; int x , y; scanf ...
- JUnit单元测试遇到的问题及解决思路
JUnit是Java单元测试框架,我们在对开发的系统进行单元测试的时候,也遇到了如何测试多个测试用例的问题. 背景:我们的所有测试用例都保存在Excel文件中,该文件包含测试用例和预期输出.我们希望 ...