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 ...
随机推荐
- analyzing problems
If you talking to a friend or talking to a family member ,you can say:what's the metter or What's go ...
- [Usaco 5.4] Telecowmunication
Description 农夫约翰的奶牛们喜欢通过电邮保持联系,于是她们建立了一个奶牛电脑网络,以便互相交流.这些机器用如下的方式发送电邮:如果存在一个由c台电脑组成的序列a1,a2,...,a(c), ...
- nxlog4go 的配置驱动
刚开始接触log4go项目时,没有注意到配置的重要性. 阅读了log4j.log4net.log4cpp.log4cplus的部分代码,发现它们都是以xml配置来驱动日志系统运行的. 多个源文件共享一 ...
- 【Python】 SQLAlchemy的初步使用
SQLAlchemy 在很多Python的web框架中都整合进了SQLAlchemy这个主要发挥ORM作用的模块.所谓ORM,就是把复杂的SQL语句给包装成更加面向对象,易于理解的样子.在操作数据库的 ...
- linux --> 系统信息命令
系统信息命令 # uname -a // # 查看内核/操作系统/CPU信息 # head -n /etc/issue // # 查看操作系统版本 # cat /proc/cpuinfo // # 查 ...
- c++ --> typedef用法总结
typedef用法总结 一.四大用途 用途1 定义类型别名,在大量使用指针的地方,typedef更方便 typedef char* PCHAR; // 一般用大写 PCHAR pa, pb; // ...
- 浅谈-RMQ
浅谈RMQ Today,我get到了一个新算法,开心....RMQ. 今天主要说一下RMQ里的ST算法(Sparse Table). RMQ(Range Minimum/Maximum Query), ...
- Android Intent 基本使用及对象构成
Intent基本使用 Intent可以理解为不同组件通信的媒介或者信使. Intent可以启动一个Activity,也可以启动一个Service,还可以发起一个广播Broadcast. 具体方法如下表 ...
- alpha-咸鱼冲刺day5
一,合照 emmmmm.自然还是没有的. 二,项目燃尽图 三,项目进展 !!!QAQ可以做到跟数据库交互了!!!!先来撒花花!(然后继续甲板) 四,问题困难 日常啥都不会,百度真心玩一年. 还得自学n ...
- 需求分析&原型设计
需求分析&原型设计 需求分析 访问软件项目真实用户 首先本项目的用户是这个需要做简单四则运算的用户(我们团队通过对家里有三四年级小学生(需要做简单四则运算)的简单采访):反映了几个主要的问题: ...