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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/387026.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/387026.shtml
英文地址,請注明出處:http://en.pswp.cn/news/387026.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

評估一個垃圾收集(GC)

在實踐中我們發現對于大多數的應用領域&#xff0c;評估一個垃圾收集(GC)算法如何根據如下兩個標準&#xff1a; 吞吐量越高算法越好暫停時間越短算法越好 首先讓我們來明確垃圾收集(GC)中的兩個術語:吞吐量(throughput)和暫停時間(pause times)。 JVM在專門的線程(GC threads…

python數據分析常用包之Scipy

Scipy轉載于:https://www.cnblogs.com/jacky912/p/10697853.html

docker容器狀態跟蹤及疑惑

一、 1 def status_test():2 container client.containers.create("comp")3 print ("create: ", container.status)4 container.start()5 print ("start: ", container.status)6 container.pause()7 print ("paus…

CAP和BASE理論

幾個名詞解釋&#xff1a; 網絡分區&#xff1a;俗稱“腦裂”。當網絡發生異常情況&#xff0c;導致分布式系統中部分節點之間的網絡延時不斷變大&#xff0c;最終導致組成分布式系統的所有節點中&#xff0c;只有部分節點之間能夠進行正常通信&#xff0c;而另一些節點則不能…

Mysql案例5:取得平均薪資最高的部門的部門名稱

一、要求&#xff1a;查詢平均薪水最高部門的部門編號 二、背景&#xff1a;當前數據庫有employee表和department表&#xff0c;數據分別如下&#xff1a; employee表&#xff1a; department表&#xff1a; 三、難點&#xff1a; 1、需要考慮最高平均薪資可能在多個部門同時出…

Spring 處理過程分析

一、處理過程分析 1、首先&#xff0c;Tomcat每次啟動時都會加載并解析/WEB-INF/web.xml文件&#xff0c;所以可以先從web.xml找突破口&#xff0c;主要代碼如下&#xff1a;<servlet ><servlet-name >spring-mvc</servlet-name><!-- servlet類 --><…

python全棧開發中級班全程筆記(第二模塊、第四章)(常用模塊導入)

python全棧開發筆記第二模塊 第四章 &#xff1a;常用模塊&#xff08;第二部分&#xff09; 一、os 模塊的 詳解 1、os.getcwd() &#xff1a;得到當前工作目錄&#xff0c;即當前python解釋器所在目錄路徑 import os j os.getcwd() # 返回當前pyt…

基于 Spring Cloud 完整的微服務架構實戰

本項目是一個基于 Spring Boot、Spring Cloud、Spring Oauth2 和 Spring Cloud Netflix 等框架構建的微服務項目。 作者&#xff1a;Sheldon地址&#xff1a;https://github.com/zhangxd1989 技術棧 Spring boot - 微服務的入門級微框架&#xff0c;用來簡化 Spring 應用的初…

mysql Invalid use of group function的解決辦法

錯誤語句&#xff1a;SELECT s.SID, s.Sname, AVG(a.score)FROM student sLEFT JOIN sc a ON s.SID a.SID WHERE AVG(a.score) > 60GROUP BY s.SID正確語句&#xff1a; SELECTs.SID,s.Sname,AVG(a.score)FROMstudent sLEFT JOIN sc a ON s.SID a.SID GROUP BYs.SID HAVIN…

ipython notebook 中 wavefile, display, Audio的使用

基于ipython notebook的 wavefile以及display, Audio的使用首先是使用的庫使用 wavfile 讀取.wav文件使用display,Audio播放聲音最近在做聲音信號處理的時候&#xff0c;使用了ipython notebook。發現相較于matlab&#xff0c;python在有關生成wave文件和播放音頻需要利用到sci…

spring 設計模式

設計模式作為工作學習中的枕邊書&#xff0c;卻時常處于勤說不用的尷尬境地&#xff0c;也不是我們時常忘記&#xff0c;只是一直沒有記憶。 今天&#xff0c;螃蟹在IT學習者網站就設計模式的內在價值做一番探討&#xff0c;并以spring為例進行講解&#xff0c;只有領略了其設計…

Ansible-----循環

with_dict迭代字典項 使用"with_dict"可以迭代字典項。迭代時&#xff0c;使用"item.key"表示字典的key&#xff0c;"item.value"表示字典的值。 ---- hosts: localhosttasks:- debug: msg"{{item.key}} & {{item.value}}"with_di…

ROS(Robot Operating System)筆記 : 1.使用launch file在gazebo中生成urdf機器人

ROS(Robot Operating System) 1.使用launch file在gazebo中生成urdf機器人 最近接觸了ROS(Robot Operating System),發現單單學習官網http://wiki.ros.org/上的教程&#xff0c;在實際操作過程中仍然會遭遇許多困難。這一系列關于ROS的文章記錄了ROS學習過程中可能遇到的問題…

[asp.net] 利用WebClient上傳圖片到遠程服務

一、客戶端 1.頁面 <form id"Form1" method"post" runat"server" enctype"multipart/form-data">     <input id"MyFile" type"file" runat"server" />     <br />     …

ROS project part 1: Ubuntu中安裝opencv包以及相應的依賴

首先在ubuntu linux上安裝opencv $ sudo apt-get install python-opencv使用ipython 驗證 opencv的安裝 $ import cv2 as cv $ print(cv.__version__)查看當前的ubuntu 版本 $ cat /etc/issue查看當前python版本 下列代碼分別用于查看python3 python2的已安裝版本 $ python…

FastReport4.6程序員手冊_翻譯

一、使用TfrxReport 組件工作1、加載并存儲報表默認情況下&#xff0c;報表窗體同項目窗體構存儲在同一個DFM文件中。多數情況下&#xff0c;無須再操作&#xff0c;因而你就不必采用特殊方法加載報表。如果你決定在文件中存儲報表窗體或者是數據庫的Blob字段&#xff08;他提供…

Python音頻信號處理 1.短時傅里葉變換及其逆變換

短時傅里葉變換及其逆變換 本篇文章主要記錄了使用python進行短時傅里葉變換&#xff0c;分析頻譜&#xff0c;以及通過頻譜實現在頻域內降低底噪的代碼及分析&#xff0c;希望可以給同樣在學習信號處理的大家一點幫助&#xff0c;也希望大家對我的文章多提意見建議。 一. 短…

Java多線程同步機制

一段synchronized的代碼被一個線程執行之前&#xff0c;他要先拿到執行這段代碼的權限&#xff0c;在 java里邊就是拿到某個同步對象的鎖&#xff08;一個對象只有一把鎖&#xff09;&#xff1b; 如果這個時候同步對象的鎖被其他線程拿走了&#xff0c;他&#xff08;這個線程…

SpringBoot與數據訪問

pom依賴&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-conne…

Python音頻信號處理 2.使用譜減法去除音頻底噪

使用譜減法去除音頻底噪 上一篇文章我主要分享了短時傅立葉變換及其逆變換在python中的實現&#xff0c;有興趣的可以閱讀一下該篇文章&#xff0c;地址如下&#xff1a; Python音頻信號處理 1.短時傅里葉變換及其逆變換 那么在本篇文章中&#xff0c;我們將利用短時傅立葉變…