---恢复内容开始---

笔者从事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源码学习(一)相关推荐

  1. ASP.NET Core 源码学习之Logging[1]:Introduction

    在ASP.NET 4.X中,我们通常使用 log4net, NLog 等来记录日志,但是当我们引用的一些第三方类库使用不同的日志框架时,就比较混乱了.而在 ASP.Net Core 中内置了日志系统, ...

  2. ASP.NET Core MVC 源码学习:MVC 启动流程详解

    前言 在 上一篇 文章中,我们学习了 ASP.NET Core MVC 的路由模块,那么在本篇文章中,主要是对 ASP.NET Core MVC 启动流程的一个学习. ASP.NET Core 是新一 ...

  3. Shiro源码学习之二

    接上一篇 Shiro源码学习之一 3.subject.login 进入login public void login(AuthenticationToken token) throws Authent ...

  4. Shiro源码学习之一

    一.最基本的使用 1.Maven依赖 <dependency><groupId>org.apache.shiro</groupId><artifactId&g ...

  5. mutations vuex 调用_Vuex源码学习(六)action和mutation如何被调用的(前置准备篇)...

    前言 Vuex源码系列不知不觉已经到了第六篇.前置的五篇分别如下: 长篇连载:Vuex源码学习(一)功能梳理 长篇连载:Vuex源码学习(二)脉络梳理 作为一个Web前端,你知道Vuex的instal ...

  6. vue实例没有挂载到html上,vue 源码学习 - 实例挂载

    前言 在学习vue源码之前需要先了解源码目录设计(了解各个模块的功能)丶Flow语法. src ├── compiler # 把模板解析成 ast 语法树,ast 语法树优化,代码生成等功能. ├── ...

  7. 2021-03-19Tomcat源码学习--WebAppClassLoader类加载机制

    Tomcat源码学习--WebAppClassLoader类加载机制 在WebappClassLoaderBase中重写了ClassLoader的loadClass方法,在这个实现方法中我们可以一窥t ...

  8. jQuery源码学习之Callbacks

    jQuery源码学习之Callbacks jQuery的ajax.deferred通过回调实现异步,其实现核心是Callbacks. 使用方法 使用首先要先新建一个实例对象.创建时可以传入参数flag ...

  9. JDK源码学习笔记——Integer

    一.类定义 public final class Integer extends Number implements Comparable<Integer> 二.属性 private fi ...

最新文章

  1. 学python的游戏app_Python教学软件
  2. openlayers地图旋转_地图切换动画#openlayers入门笔记#
  3. ublox Android 定位超时,[RK3288] [Android 7.1] u-blox GPS调试
  4. RadioButtonList Enabled=false 文字背景
  5. 华为机试——数字颠倒
  6. Linux struct itimerval用法
  7. 《大数据管理概论》一2.5 知识融合技术
  8. Spring中使用集成MongoDB Client启动时报错:rc: 48
  9. 线阵相机工作模式解读
  10. python访问带密码的共享文件夹_设置带密码和读写权限的共享文件夹 - Hakka
  11. oracle对用户的管理
  12. DevExpress中使用ChartControl绘制折线图和导出图表为Excel文件
  13. 论文中c语言程序的格式,毕业论文程序代码格式_毕业论文范本_论文的标准格式模板...
  14. idea破解永久免费
  15. python 证件照换底、抠像 百度人体分析
  16. Linux之企业实训篇——haproxy与pacemaker实现高可用负载均衡
  17. linux下硬件检测工具,Linux硬件检测工具
  18. android sim卡槽,一加5怎么装卡/插卡 一加手机5 SIM卡安装图文教程
  19. 机器学习项目实战——10决策树算法之动物分类
  20. golang kv存储引擎

热门文章

  1. VS2010 RTM
  2. 泉州经贸职业技术学院计算机系,部门简介-泉州经贸职业技术学院网络电教中心...
  3. mysql count 不等于_Mysql 不同的 count 区别
  4. python 编辑excel需要什么包_Python 中操作EXCEL表格的包
  5. Nginx 实现网站 http、https 配置
  6. Linux GRUB 引导Win 7 ---- error: invalid EFI file path
  7. 《MySQL——外部检测与内部统计 判断 主库是否出现问题》
  8. JavaScript中的数组
  9. Java类class forName()方法及示例
  10. 实训09.11:数据库一些简单操作