aspnetcore源码学习(一)
---恢复内容开始---
笔者从事netcore相关项目开发已经大半年了,从netcore 1.0到现在3.0大概经过了3年左右的时间,记得netcore刚出来的时候国内相关的学习资料缺乏,限制于外语不大熟练的限制国外的相关书籍看起来相当吃力,于是在当当网上买了一本价值70多的入门书籍,买回来却发现内容都是挂羊头卖狗肉,深深地鄙视这些为了赚钱不顾内容的作者。如今网上相关的学习资料也相当多,笔者也趁着现在不忙,再来学习一下aspnetcore的源码,文章中所用的源码版本是3.0,如果读者下的源码是3.0以下,有些函数会有所区别。
下图是笔者整理的一个简单类图,以助自己理解源码。
对象介绍
WebHostBuilder:负责初始化环境变量,默认设置,指定startup类,创建servicecollection,读取configuration,创建基础服务并注入到DI,加载主机配置(hostingStartup),最主要的创建webhost。
WebHost:站点主机,加载应用服务,加载应用中间件,开始和停止站点
WebHostBuilderContext: 上下文,包含环境变量和默认值
WebHostOptions: 创建webhost的时候使用的参数
ServiceCollection: 所有服务的储存的集合,添加删除服务
serviceprovider: 获得服务的实例
serviceDescriptor: 服务的描述类,所有的服务最后都是转化成该类后注入DI
关键的对象介绍完了,下面我们来看一下web站点是如何运行起来的。
- main函数中创建WebHostBuilder对象
- 指定startup
public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType){var startupAssemblyName = startupType.GetTypeInfo().Assembly.GetName().Name;hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName);// Light up the GenericWebHostBuilder implementationif (hostBuilder is ISupportsStartup supportsStartup){return supportsStartup.UseStartup(startupType);}return hostBuilder.ConfigureServices(services =>{if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo())){services.AddSingleton(typeof(IStartup), startupType);}else{services.AddSingleton(typeof(IStartup), sp =>{var hostingEnvironment = sp.GetRequiredService<IHostEnvironment>();return new ConventionBasedStartup(StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName));});}});}
从代码可以看到,如果这个startup是继承自IStartup,直接注入到DI,如果不是,则用ConventtionBaseStartup进行包装,而该类是继承自IStartup。微软默认的startup类不继承IStartup的,具体的实现细节和原因在后面会具体说明。
- 加载系统的默认的配置文件,可以通过 ConfigureAppConfiguration扩展方法将自己的配置文件加载到容器里面
public static IWebHostBuilder ConfigureAppConfiguration(this IWebHostBuilder hostBuilder, Action<IConfigurationBuilder> configureDelegate){return hostBuilder.ConfigureAppConfiguration((context, builder) => configureDelegate(builder));}
- 创建WebHostBuild上下文
- 加载web主机配置,寻找程序集中贴有HostingStartupAttribute标签的类并调用Configure方法,在WebHost实例化之前预留的钩子,由于笔者也没有用到过这个功能,就不多赘述了。
foreach (var assemblyName in _options.GetFinalHostingStartupAssemblies().Distinct(StringComparer.OrdinalIgnoreCase)){try{var assembly = Assembly.Load(new AssemblyName(assemblyName));foreach (var attribute in assembly.GetCustomAttributes<HostingStartupAttribute>()){var hostingStartup = (IHostingStartup)Activator.CreateInstance(attribute.HostingStartupType);hostingStartup.Configure(this);}}catch (Exception ex){...}}
- 创建servicecollection 类,此类存在于整个webhost生命周期,所有的应用服务和系统服务都存储在该类中
- 添加系统服务,包括环境变量,日志服务,配置服务,监听服务等等
var services = new ServiceCollection();services.AddSingleton(_options);services.AddSingleton<IWebHostEnvironment>(_hostingEnvironment);services.AddSingleton<IHostEnvironment>(_hostingEnvironment);
#pragma warning disable CS0618 // Type or member is obsoleteservices.AddSingleton<AspNetCore.Hosting.IHostingEnvironment>(_hostingEnvironment);services.AddSingleton<Extensions.Hosting.IHostingEnvironment>(_hostingEnvironment);
#pragma warning restore CS0618 // Type or member is obsoleteservices.AddSingleton(_context);var builder = new ConfigurationBuilder().SetBasePath(_hostingEnvironment.ContentRootPath).AddConfiguration(_config);_configureAppConfigurationBuilder?.Invoke(_context, builder);var configuration = builder.Build();services.AddSingleton<IConfiguration>(configuration);_context.Configuration = configuration;var listener = new DiagnosticListener("Microsoft.AspNetCore");services.AddSingleton<DiagnosticListener>(listener);services.AddSingleton<DiagnosticSource>(listener);services.AddTransient<IApplicationBuilderFactory, ApplicationBuilderFactory>();services.AddTransient<IHttpContextFactory, DefaultHttpContextFactory>();services.AddScoped<IMiddlewareFactory, MiddlewareFactory>();services.AddOptions();services.AddLogging();services.AddTransient<IServiceProviderFactory<IServiceCollection>, DefaultServiceProviderFactory>();
- 创建 ServiceProvider 类,该类依赖于ServiceCollection,并为其中的服务提供实例
- 创建使用 ServiceProvider和 ServiceCollection类创建 webhost实例并初始化实例
var host = new WebHost(applicationServices,hostingServiceProvider,_options,_config,hostingStartupErrors);try{host.Initialize();...return host;}
初始化实例的时候会调用EnsureApplicationServices的方法。看到_startup.ConfigureServices了没有,对这个就是我们在startup中写的方法。
private void EnsureApplicationServices(){if (_applicationServices == null){EnsureStartup();_applicationServices = _startup.ConfigureServices(_applicationServiceCollection);}}
可是他是如何找到这个方法的呢,再看一下EnsureStartup方法,原来是实例化了我们在存在servicecollection中的startup类,前面我们说过,这个类ConventionBasedStartup封装了,所以这里获取的也ConventionBasedStartup类的实例。
private void EnsureStartup(){if (_startup != null){return;}_startup = _hostingServiceProvider.GetService<IStartup>();...}
再让我们看下ConventionBasedStartup的结构,实际上就是将该类作为一个代理来调用我们startup类中的方法
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;}}}
- 主机实例已创建并且相关服务已初始化完成,这时候就可以start()了。可能你会想可是我的中间件还没加载,是的 ,start的时候就是加载中间件并创建监听,让我们来看一下代码
public virtual async Task StartAsync(CancellationToken cancellationToken = default){...var application = BuildApplication();...}
private RequestDelegate BuildApplication(){try{_applicationServicesException?.Throw();EnsureServer();var builderFactory = _applicationServices.GetRequiredService<IApplicationBuilderFactory>();var builder = builderFactory.CreateBuilder(Server.Features);builder.ApplicationServices = _applicationServices; Action<IApplicationBuilder> configure = _startup.Configure;...configure(builder);return builder.Build();}}
发现代码中的_startup.Configure了吗!这个就是调用ConventionBasedStartup这个代理类的c方法,也就是我们startup中的Configure方法。这时候笔者当时也产生了一个疑问,这里的委托只有一个参数,可是在实际应用的时候都会加入很多参数,想这样public void Configure(IApplicationBuilder app, IHostingEnvironment env,IXxxx xxxx)。猜想是不是初始化ConventionBasedStartup类的时候做了什么封装,重新回到UseStartup方法
return new ConventionBasedStartup(StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName));
public static StartupMethods LoadMethods(IServiceProvider hostingServiceProvider, Type startupType, string environmentName){var configureMethod = FindConfigureDelegate(startupType, environmentName);var servicesMethod = FindConfigureServicesDelegate(startupType, environmentName);var configureContainerMethod = FindConfigureContainerDelegate(startupType, environmentName);object instance = null;if (!configureMethod.MethodInfo.IsStatic || (servicesMethod != null && !servicesMethod.MethodInfo.IsStatic)){instance = ActivatorUtilities.GetServiceOrCreateInstance(hostingServiceProvider, startupType);}// The type of the TContainerBuilder. If there is no ConfigureContainer method we can just use object as it's not// going to be used for anything.var type = configureContainerMethod.MethodInfo != null ? configureContainerMethod.GetContainerType() : typeof(object);var builder = (ConfigureServicesDelegateBuilder) Activator.CreateInstance(typeof(ConfigureServicesDelegateBuilder<>).MakeGenericType(type),hostingServiceProvider,servicesMethod,configureContainerMethod,instance);return new StartupMethods(instance, configureMethod.Build(instance), builder.Build());}
哈哈果然 LoadMethods方法将我们写在StartUp中的方法进行了封装并创建了一个新的委托,到此我们也就明白为什么官方推荐的startup类不是继承了Istartup接口的,继承了接口的类在调用时没法将DI中的类注入到方法参数中去,需要自己去获取DI中的实例,笔者设想control类中的注入也是同样的思想实现的。现在我们的所有中间件和服务已经全部加载进入WebHost中了。
转载于:https://www.cnblogs.com/galen-huang/p/10693520.html
aspnetcore源码学习(一)相关推荐
- ASP.NET Core 源码学习之Logging[1]:Introduction
在ASP.NET 4.X中,我们通常使用 log4net, NLog 等来记录日志,但是当我们引用的一些第三方类库使用不同的日志框架时,就比较混乱了.而在 ASP.Net Core 中内置了日志系统, ...
- ASP.NET Core MVC 源码学习:MVC 启动流程详解
前言 在 上一篇 文章中,我们学习了 ASP.NET Core MVC 的路由模块,那么在本篇文章中,主要是对 ASP.NET Core MVC 启动流程的一个学习. ASP.NET Core 是新一 ...
- Shiro源码学习之二
接上一篇 Shiro源码学习之一 3.subject.login 进入login public void login(AuthenticationToken token) throws Authent ...
- Shiro源码学习之一
一.最基本的使用 1.Maven依赖 <dependency><groupId>org.apache.shiro</groupId><artifactId&g ...
- mutations vuex 调用_Vuex源码学习(六)action和mutation如何被调用的(前置准备篇)...
前言 Vuex源码系列不知不觉已经到了第六篇.前置的五篇分别如下: 长篇连载:Vuex源码学习(一)功能梳理 长篇连载:Vuex源码学习(二)脉络梳理 作为一个Web前端,你知道Vuex的instal ...
- vue实例没有挂载到html上,vue 源码学习 - 实例挂载
前言 在学习vue源码之前需要先了解源码目录设计(了解各个模块的功能)丶Flow语法. src ├── compiler # 把模板解析成 ast 语法树,ast 语法树优化,代码生成等功能. ├── ...
- 2021-03-19Tomcat源码学习--WebAppClassLoader类加载机制
Tomcat源码学习--WebAppClassLoader类加载机制 在WebappClassLoaderBase中重写了ClassLoader的loadClass方法,在这个实现方法中我们可以一窥t ...
- jQuery源码学习之Callbacks
jQuery源码学习之Callbacks jQuery的ajax.deferred通过回调实现异步,其实现核心是Callbacks. 使用方法 使用首先要先新建一个实例对象.创建时可以传入参数flag ...
- JDK源码学习笔记——Integer
一.类定义 public final class Integer extends Number implements Comparable<Integer> 二.属性 private fi ...
最新文章
- 学python的游戏app_Python教学软件
- openlayers地图旋转_地图切换动画#openlayers入门笔记#
- ublox Android 定位超时,[RK3288] [Android 7.1] u-blox GPS调试
- RadioButtonList Enabled=false 文字背景
- 华为机试——数字颠倒
- Linux struct itimerval用法
- 《大数据管理概论》一2.5 知识融合技术
- Spring中使用集成MongoDB Client启动时报错:rc: 48
- 线阵相机工作模式解读
- python访问带密码的共享文件夹_设置带密码和读写权限的共享文件夹 - Hakka
- oracle对用户的管理
- DevExpress中使用ChartControl绘制折线图和导出图表为Excel文件
- 论文中c语言程序的格式,毕业论文程序代码格式_毕业论文范本_论文的标准格式模板...
- idea破解永久免费
- python 证件照换底、抠像 百度人体分析
- Linux之企业实训篇——haproxy与pacemaker实现高可用负载均衡
- linux下硬件检测工具,Linux硬件检测工具
- android sim卡槽,一加5怎么装卡/插卡 一加手机5 SIM卡安装图文教程
- 机器学习项目实战——10决策树算法之动物分类
- golang kv存储引擎
热门文章
- VS2010 RTM
- 泉州经贸职业技术学院计算机系,部门简介-泉州经贸职业技术学院网络电教中心...
- mysql count 不等于_Mysql 不同的 count 区别
- python 编辑excel需要什么包_Python 中操作EXCEL表格的包
- Nginx 实现网站 http、https 配置
- Linux GRUB 引导Win 7 ---- error: invalid EFI file path
- 《MySQL——外部检测与内部统计 判断 主库是否出现问题》
- JavaScript中的数组
- Java类class forName()方法及示例
- 实训09.11:数据库一些简单操作