源码里没有configure_深入源码理解.NET Core中Startup的注册及运行
開發(fā).NET Core應(yīng)用,直接映入眼簾的就是Startup類和Program類,它們是.NET Core應(yīng)用程序的起點(diǎn)。通過使用Startup,可以配置化處理所有向應(yīng)用程序所做的請(qǐng)求的管道,同時(shí)也可以減少.NET應(yīng)用程序?qū)我环?wù)器的依賴性,使我們?cè)诟蟪潭壬蠈W⒂诿嫦蚨喾?wù)器為中心的開發(fā)模式。
目錄:
Startup討論
Starup所承擔(dān)的角色
Startup編寫規(guī)范
ConfigureServices
Configure
擴(kuò)展Startup方法
深入源碼查看Startup是如何注冊(cè)和執(zhí)行的
UseStartup源碼
創(chuàng)建Startup實(shí)例
ConfigureServices和Configure
Starup所承擔(dān)的角色
Startup類是ASP.NET Core程序中所必須的,可以使用多種修飾符(public、protect,private、internal),作為ASP.NET Core應(yīng)用程序的入口,它包含與應(yīng)用程序相關(guān)配置的功能或者說是接口。
雖然在程序里我們使用的類名就是Startup,但是需要注意的是,Startup是一個(gè)抽象概念,你完全可以名稱成其他的,比如MyAppStartup或者其他的什么名稱,只要你在Program類中啟動(dòng)你所定義的啟動(dòng)類即可。
以下是基于ASP.NET Core Preview 3模板中提供的寫法:
1: public class Program 2: { 3: public static void Main(string[] args) 4: { 5: CreateHostBuilder(args).Build().Run(); 6: } 7: ? 8: public static IHostBuilder CreateHostBuilder(string[] args) => 9: Host.CreateDefaultBuilder(args) 10: .ConfigureWebHostDefaults(webBuilder => 11: { 12: webBuilder.UseStartup(); 13: }); 14: }不管你命名成什么,只要將webBuilder.UseStartup<>()中的泛型類配置成你定義的入口類即可;
Startup編寫規(guī)范
下面是ASP.NET Core 3.0 Preview 3模板中Startup的寫法:
1: // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 2: public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 3: { 4: if (env.IsDevelopment()) 5: { 6: app.UseDeveloperExceptionPage(); 7: } 8: else 9: { 10: // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 11: app.UseHsts(); 12: } 13: ? 14: app.UseHttpsRedirection(); 15: ? 16: app.UseRouting(routes => 17: { 18: routes.MapControllers(); 19: }); 20: ? 21: app.UseAuthorization(); 22: }通過以上代碼可以知道,Startup類中一般包括
構(gòu)造函數(shù):通過我們以前的開發(fā)經(jīng)驗(yàn),我們可以知道,該構(gòu)造方法可以包括多個(gè)對(duì)象
IConfiguration:表示一組鍵/值應(yīng)用程序配置屬性。
IApplicationBuilder:是一個(gè)包含與當(dāng)前環(huán)境相關(guān)的屬性和方法的接口。它用于獲取應(yīng)用程序中的環(huán)境變量。
IHostingEnvironment:是一個(gè)包含與運(yùn)行應(yīng)用程序的Web宿主環(huán)境相關(guān)信息的接口。使用這個(gè)接口方法,我們可以改變應(yīng)用程序的行為。
ILoggerFactory:是為ASP.NET Core中的日志記錄系統(tǒng)提供配置的接口。它還創(chuàng)建日志系統(tǒng)的實(shí)例。
ConfigureServices
Configure
Startup在創(chuàng)建服務(wù)時(shí),會(huì)執(zhí)行依賴項(xiàng)注冊(cè)服務(wù),以便在應(yīng)用程序的其它地方使用這些依賴項(xiàng)。ConfigureServices 用于注冊(cè)服務(wù),Configure 方法允許我們向HTTP管道添加中間件和服務(wù)。這就是ConfigureServices先于Configure 之前調(diào)用的原因。
ConfigureServices
該方法時(shí)可選的,非強(qiáng)制約束,它主要用于對(duì)依賴注入或ApplicationServices在整個(gè)應(yīng)用中的支持,該方法必須是public的,其典型模式是調(diào)用所有?Add{Service} 方法,主要場(chǎng)景包括實(shí)體框架、認(rèn)證和 MVC 注冊(cè)服務(wù):
1: services.AddDbContext(options =>options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); 2: services.AddDefaultIdentity().AddDefaultUI(UIFramework.Bootstrap4).AddEntityFrameworkStores(); 3: services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); 4: // Add application services.此處主要是注冊(cè)IOC服務(wù) 5: services.AddTransient(); 6: services.AddTransient();Configure
該方法主要用于定義應(yīng)用程序?qū)γ總€(gè)HTTP請(qǐng)求的響應(yīng)方式,即我們可以控制ASP.NET管道,還可用于在HTTP管道中配置中間件。請(qǐng)求管道中的每個(gè)中間件組件負(fù)責(zé)調(diào)用管道中的下一個(gè)組件,或在適當(dāng)情況下使鏈發(fā)生短路。 如果中間件鏈中未發(fā)生短路,則每個(gè)中間件都有第二次機(jī)會(huì)在將請(qǐng)求發(fā)送到客戶端前處理該請(qǐng)求。
該方法接受IApplicationBuilder作為參數(shù),同時(shí)還可以接收其他一些可選參數(shù),如IHostingEnvironment和ILoggerFactory。
一般而言,只要將服務(wù)注冊(cè)到configureServices方法中時(shí),都可以在該方法中使用。
1: app.UseDeveloperExceptionPage(); 2: app.UseHsts(); 3: app.UseHttpsRedirection(); 4: app.UseRouting(routes => 5: { 6: routes.MapControllers(); 7: }); 8: app.UseAuthorization();擴(kuò)展Startup方法
使用IStartupFilter來對(duì)Startup功能進(jìn)行擴(kuò)展,在應(yīng)用的Configure中間件管道的開頭或末尾使用IStartupFilter來配置中間件。IStartupFilter有助于確保當(dāng)庫在應(yīng)用請(qǐng)求處理管道的開端或末尾添加中間件的前后運(yùn)行中間件。
以下是IStartupFilter的源代碼,通過源代碼我們可以知道,該接口有一個(gè)Action類型,并命名為Configure的方法。由于傳入?yún)?shù)類型和返回類型一樣,這就保證了擴(kuò)展的傳遞性及順序性,具體的演示代碼,可以參數(shù)MSDN
1: using System; 2: using Microsoft.AspNetCore.Builder; 3: 4: namespace Microsoft.AspNetCore.Hosting 5: { 6: public interface IStartupFilter 7: { 8: Action Configure(Action next); 9: } 10: }此段文字,只是我想深入了解其內(nèi)部機(jī)制而寫的,如果本身也不了解,其實(shí)是不影響我們正常編寫.NET Core應(yīng)用的。
UseStartup源碼
ASP.NET Core通過調(diào)用IWebHostBuilder.UseStartup方法,傳入Startup類型,注意開篇就已經(jīng)說過Startup是一個(gè)抽象概念,我們看下源代碼:
1: /// 2: /// Specify the startup type to be used by the web host. 3: /// 4: /// The to configure. 5: /// The to be used. 6: /// The . 7: public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType) 8: { 9: var startupAssemblyName = startupType.GetTypeInfo().Assembly.GetName().Name; 10: 11: hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName); 12: 13: // Light up the GenericWebHostBuilder implementation 14: if (hostBuilder is ISupportsStartup supportsStartup) 15: { 16: return supportsStartup.UseStartup(startupType); 17: } 18: 19: return hostBuilder 20: .ConfigureServices(services => 21: { 22: if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo())) 23: { 24: services.AddSingleton(typeof(IStartup), startupType); 25: } 26: else 27: { 28: services.AddSingleton(typeof(IStartup), sp => 29: { 30: var hostingEnvironment = sp.GetRequiredService(); 31: return new ConventionBasedStartup(StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName)); 32: }); 33: } 34: }); 35: } 36: 37: /// 38: /// Specify the startup type to be used by the web host. 39: /// 40: /// The to configure. 41: /// The type containing the startup methods for the application. 42: /// The . 43: public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder) where TStartup :class 44: { 45: return hostBuilder.UseStartup(typeof(TStartup)); 46: }創(chuàng)建Startup實(shí)例
1: /// 2: /// Adds a delegate for configuring additional services for the host or web application. This may be called 3: /// multiple times. 4: /// 5: /// A delegate for configuring the . 6: /// The . 7: public IWebHostBuilder ConfigureServices(Action configureServices) 8: { 9: if (configureServices == null) 10: { 11: throw new ArgumentNullException(nameof(configureServices)); 12: } 13: ? 14: return ConfigureServices((_, services) => configureServices(services)); 15: } 16: ? 17: /// 18: /// Adds a delegate for configuring additional services for the host or web application. This may be called 19: /// multiple times. 20: /// 21: /// A delegate for configuring the . 22: /// The . 23: public IWebHostBuilder ConfigureServices(Action configureServices) 24: { 25: _configureServices += configureServices; 26: return this; 27: }關(guān)于ConfigureServices的定義及注冊(cè)方式,是在IWebHostBuilder.ConfigureServices實(shí)現(xiàn)的,同時(shí)可以注意一下25行代碼,向大家說明了多次注冊(cè)Startup的ConfigureServices方法時(shí),會(huì)合并起來的根源。此處抽象委托用的也非常多。
該類里面還有Build方法,我就不貼出代碼了,只需要知道,主進(jìn)程在此處開始了。接下來一個(gè)比較重要的方法,是BuildCommonServices,它向當(dāng)前ServiceCollection中添加一些公共框架級(jí)服務(wù),以下是部分代碼,具體代碼請(qǐng)查看WebHostBuilder。
1: try 2: { 3: var startupType = StartupLoader.FindStartupType(_options.StartupAssembly, _hostingEnvironment.EnvironmentName); 4: ? 5: if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo())) 6: { 7: services.AddSingleton(typeof(IStartup), startupType); 8: } 9: else 10: { 11: services.AddSingleton(typeof(IStartup), sp => 12: { 13: var hostingEnvironment = sp.GetRequiredService(); 14: var methods = StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName); 15: return new ConventionBasedStartup(methods); 16: }); 17: } 18: } 19: catch (Exception ex) 20: { 21: var capture = ExceptionDispatchInfo.Capture(ex); 22: services.AddSingleton(_ => 23: { 24: capture.Throw(); 25: return null; 26: }); 27: }由此可見,如果我們的Startup類直接實(shí)現(xiàn)IStartup,它可以并且將直接注冊(cè)為IStartup的實(shí)現(xiàn)類型。只不過ASP.NET Core模板代碼并沒有實(shí)現(xiàn)IStartup,它更多的是一種約定,并通過DI調(diào)用委托,依此調(diào)用Startup內(nèi)的構(gòu)造函數(shù)還有另外兩個(gè)方法。同時(shí)上述代碼還展示了如何創(chuàng)建Startup類型,就是用到了靜態(tài)方法StartupLoader.LoadMethods類生成StartupMethods實(shí)例。ConfigureServices和Configure
當(dāng)WebHost初始化時(shí),框架會(huì)去查找相應(yīng)的方法,這里,我們主要查看源代碼,其中的核心方法是StartupLoader.FindMethods 1: private static MethodInfo FindMethod(Type startupType, string methodName, string environmentName, Type returnType = null, bool required = true) 2: { 3: var methodNameWithEnv = string.Format(CultureInfo.InvariantCulture, methodName, environmentName); 4: var methodNameWithNoEnv = string.Format(CultureInfo.InvariantCulture, methodName, ""); 5: ? 6: var methods = startupType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); 7: var selectedMethods = methods.Where(method => method.Name.Equals(methodNameWithEnv, StringComparison.OrdinalIgnoreCase)).ToList(); 8: if (selectedMethods.Count > 1) 9: { 10: throw new InvalidOperationException(string.Format("Having multiple overloads of method '{0}' is not supported.", methodNameWithEnv)); 11: } 12: if (selectedMethods.Count == 0) 13: { 14: selectedMethods = methods.Where(method => method.Name.Equals(methodNameWithNoEnv, StringComparison.OrdinalIgnoreCase)).ToList(); 15: if (selectedMethods.Count > 1) 16: { 17: throw new InvalidOperationException(string.Format("Having multiple overloads of method '{0}' is not supported.", methodNameWithNoEnv)); 18: } 19: } 20: ? 21: var methodInfo = selectedMethods.FirstOrDefault(); 22: if (methodInfo == null) 23: { 24: if (required) 25: { 26: throw new InvalidOperationException(string.Format("A public method named '{0}' or '{1}' could not be found in the '{2}' type.", 27: methodNameWithEnv, 28: methodNameWithNoEnv, 29: startupType.FullName)); 30: ? 31: } 32: return null; 33: } 34: if (returnType != null && methodInfo.ReturnType != returnType) 35: { 36: if (required) 37: { 38: throw new InvalidOperationException(string.Format("The '{0}' method in the type '{1}' must have a return type of '{2}'.", 39: methodInfo.Name, 40: startupType.FullName, 41: returnType.Name)); 42: } 43: return null; 44: } 45: return methodInfo; 46: }它查找的第一個(gè)委托是ConfigureDelegate,該委托將用于構(gòu)建應(yīng)用程序的中間件管道。FindMethod完成了大部分工作,具體的代碼請(qǐng)查看StartupLoader。此方法根據(jù)傳遞給它的methodName參數(shù)在Startup類中查找響應(yīng)的方法。我們知道,Startup的定義更多的是約定,所以會(huì)去查找Configure和ConfigureServices。當(dāng)然,通過源代碼我還知道,除了提供標(biāo)準(zhǔn)的“Configure”方法之外,我們還可以通過環(huán)境配置找到響應(yīng)的Configure和ConfigureServices。根本來說,我們最終查找到的是ConfigureContainerDelegate。接下來,一個(gè)比較重要的方法是LoadMethods 1: public static StartupMethods LoadMethods(IServiceProvider hostingServiceProvider, Type startupType, string environmentName) 2: { 3: var configureMethod = FindConfigureDelegate(startupType, environmentName); 4: ? 5: var servicesMethod = FindConfigureServicesDelegate(startupType, environmentName); 6: var configureContainerMethod = FindConfigureContainerDelegate(startupType, environmentName); 7: ? 8: object instance = null; 9: if (!configureMethod.MethodInfo.IsStatic || (servicesMethod != null && !servicesMethod.MethodInfo.IsStatic)) 10: { 11: instance = ActivatorUtilities.GetServiceOrCreateInstance(hostingServiceProvider, startupType); 12: } 13: ? 14: // The type of the TContainerBuilder. If there is no ConfigureContainer method we can just use object as it's not 15: // going to be used for anything. 16: var type = configureContainerMethod.MethodInfo != null ? configureContainerMethod.GetContainerType() : typeof(object); 17: ? 18: var builder = (ConfigureServicesDelegateBuilder) Activator.CreateInstance( 19: typeof(ConfigureServicesDelegateBuilder<>).MakeGenericType(type), 20: hostingServiceProvider, 21: servicesMethod, 22: configureContainerMethod, 23: instance); 24: ? 25: return new StartupMethods(instance, configureMethod.Build(instance), builder.Build()); 26: }該方法通過查找對(duì)應(yīng)的方法,由于Startup并未在DI中注冊(cè),所以會(huì)調(diào)用GetServiceOrCreateInstance創(chuàng)建一個(gè)Startup實(shí)例,此時(shí)構(gòu)造函數(shù)也在此得到解析。通過一系列的調(diào)用,最終到達(dá)了ConfigureServicesBuilder.Invoke里面。Invoke方法使用反射來獲取和檢查在Startup類上定義的ConfigureServices方法所需的參數(shù)。 1: private IServiceProvider InvokeCore(object instance, IServiceCollection services) 2: { 3: if (MethodInfo == null) 4: { 5: return null; 6: } 7: ? 8: // Only support IServiceCollection parameters 9: var parameters = MethodInfo.GetParameters(); 10: if (parameters.Length > 1 || 11: parameters.Any(p => p.ParameterType != typeof(IServiceCollection))) 12: { 13: throw new InvalidOperationException("The ConfigureServices method must either be parameterless or take only one parameter of type IServiceCollection."); 14: } 15: ? 16: var arguments = new object[MethodInfo.GetParameters().Length]; 17: ? 18: if (parameters.Length > 0) 19: { 20: arguments[0] = services; 21: } 22: ? 23: return MethodInfo.Invoke(instance, arguments) as IServiceProvider; 24: }最后我們來看一下ConfigureBuilder類,它需要一個(gè)Action委托變量,其中包含每個(gè)IStartupFilter的一組包裝的Configure方法,最后一個(gè)是Startup.Configure方法的委托。此時(shí),所調(diào)用的配置鏈?zhǔn)紫让械氖茿utoRequestServicesStartupFilter.Configure方法。并將該委托鏈作為下一個(gè)操作,之后會(huì)調(diào)用ConventionBasedStartup.Configure方法。這將在其本地StartupMethods對(duì)象上調(diào)用ConfigureDelegate。
1: private void Invoke(object instance, IApplicationBuilder builder) 2: { 3: // Create a scope for Configure, this allows creating scoped dependencies 4: // without the hassle of manually creating a scope. 5: using (var scope = builder.ApplicationServices.CreateScope()) 6: { 7: var serviceProvider = scope.ServiceProvider; 8: var parameterInfos = MethodInfo.GetParameters(); 9: var parameters = new object[parameterInfos.Length]; 10: for (var index = 0; index < parameterInfos.Length; index++) 11: { 12: var parameterInfo = parameterInfos[index]; 13: if (parameterInfo.ParameterType == typeof(IApplicationBuilder)) 14: { 15: parameters[index] = builder; 16: } 17: else 18: { 19: try 20: { 21: parameters[index] = serviceProvider.GetRequiredService(parameterInfo.ParameterType); 22: } 23: catch (Exception ex) 24: { 25: throw new Exception(string.Format( 26: "Could not resolve a service of type '{0}' for the parameter '{1}' of method '{2}' on type '{3}'.", 27: parameterInfo.ParameterType.FullName, 28: parameterInfo.Name, 29: MethodInfo.Name, 30: MethodInfo.DeclaringType.FullName), ex); 31: } 32: } 33: } 34: MethodInfo.Invoke(instance, parameters); 35: } 36: }Startup.Configure方法會(huì)調(diào)用ServiceProvider所解析的相應(yīng)的參數(shù),該方法還可以使用IApplicationBuilder將中間件添加到應(yīng)用程序管道中。最終的RequestDelegate是從IApplicationBuilder構(gòu)建并返回的,至此WebHost初始化完成。
原文地址:https://www.cnblogs.com/edison0621/p/10743228.html
.NET社區(qū)新聞,深度好文,歡迎訪問公眾號(hào)文章匯總?http://www.csharpkit.com?
總結(jié)
以上是生活随笔為你收集整理的源码里没有configure_深入源码理解.NET Core中Startup的注册及运行的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 工业计算机远程控制,基于SOCKET技术
- 下一篇: getbook netty实战_Nett