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 ...
随机推荐
- AngularJs踩过的坑
1. 使用checkbox和radio时, 不能使用ng-model直接绑定原始对象, 需要绑定一个对象的某个子属性, 才能把值绑定到scope <label><input type ...
- 001-List,数组,Set,Map属性的映射
hibernate.cfg.xml: <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configur ...
- React 系列文章(1): npm 手动搭建React 运行实例 (新手必看)
摘 要 刚接触React 开发, 在摸索中构建react 运行环境,总会遇到各种坑:本文,将用最短时间解决webpack+react 环境搭建问题. 1.如果你还没有React基础 看这里. 2.如果 ...
- MySQL数据库学习三 数据库对象和基本操作
3.1 数据库和数据对象 1. 系统数据库 information_schema:主要存储系统中的一些数据库对象信息,如用户列表信息.列信息.权限信息.字符集信息和分区信息等. performance ...
- 设计模式 --> (17)状态模式
状态模式 允许一个对象在其内部状态改变时改变它的行为.对象看起来似乎修改了它的类.它有两种使用情况: (1)一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为. (2)一个操作中 ...
- 用SQL语言操作数据
使用T-SQL插入数据(一)SQL是什么?Structured Query Language :结构化查询语言T-SQL:Transact-SQLT-SQL是SQL的增强版对功能进行了扩充 ...
- 如何使用maven搭建web项目
博客园注册了有二十多天了,还没有写过博客,今天就发一篇,也便于后面查找笔记. 我个人已经做了几年的java web开发了,由于所在的公司是业务型公司,用的都是一些老旧的稳定技术,很少接触到稍微新点的内 ...
- 关于如何在mac系统上安装Git并在码市上建立项目
对Git一窍不通,为了在mac系统上安装Git,查了很多资料,走了很多弯路,一切搞定后发现其实很简单. 1.在https://brew.sh上按要求安装Homebrew. 2.在电脑终端键入brew ...
- 201621123035 《Java程序设计》第1周学习总结
1.本周学习总结 本周学习内容:Java平台概论.认识JDK规范与操作.了解JVM.JRE与JDK.撰写Java原始码.path是什么 关键词:JVM.JRE.JDK 联系:JVM是Java虚拟机的缩 ...
- I/O多路转接之poll 函数
poll 一.poll()函数: 这个函数是某些Unix系统提供的用于执行与select()函数同等功能的函数,自认为poll和select大同小异,下面是这个函数的声明: #include < ...