[Abp vNext 原始碼分析] - 1. 框架啟動流程分析
一、簡要說明
本片文章主要剖析與講解 Abp vNext 在 Web API 專案下的啟動流程,讓大家瞭解整個 Abp vNext 框架是如何運作的。總的來說 ,Abp vNext 比起 ABP 框架更加精簡。因為在 vNext 版本當中,原來歸屬於 Abp 庫的許多內建的基本元件 (組織單元、攔截器等) 被拆分成了單獨的模組,這樣我們來看它整個啟動流程就更加地直觀清晰。
二、原始碼分析
要分析其原始碼,我這裡是從他官方的 Demo 模板入手的,你可以在https://abp.io 上構建你自己的模板專案。工具上我使用的是 Jetbrains 家的 Rider,配置好符號伺服器(External Symbols Server),我們就能夠直接除錯其底層原始碼。(因為 Abp vNext 專案使用了 Source Link)
2.1 Startup 檔案的入口點
這裡我選擇的專案是 Web API,直接來到其Startup.cs
檔案,我們就可以看到在Startup
類當中的Configure()
與ConfigureService()
方法內部我們注入並啟用了 Abp vNext 框架。
public class Startup { public IServiceProvider ConfigureServices(IServiceCollection services) { // 注入 Abp 相關的服務。 services.AddApplication<DemoAppModule>(options => { options.UseAutofac(); }); // 接管自帶的 IoC Container。 return services.BuildServiceProviderFromFactory(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { // 配置 ASP.NET Core Mvc 相關引數。 app.InitializeApplication(); } }
在上面我們可以看到,ABP vNext 在注入服務的時候支援傳入一個Action<AbpApplicationCreationOptions>
委託。上述程式碼中,這個委託內部使用了UseAutoFac()
將 AutoFac 的容器注入到了 MS IoC 當中,關於這塊程式碼下文會著重講解。
2.2 Abp 服務註冊
在上一節看到的服務註冊程式碼,是通過擴充套件IServiceCollection
介面編寫的一個擴充套件方法實現的,在方法內部是通過AbpApplicationFactory
靜態工廠來建立一個AbpApplicationBase
例項。
public static class ServiceCollectionApplicationExtensions { public static IAbpApplicationWithExternalServiceProvider AddApplication<TStartupModule>( [NotNull] this IServiceCollection services, [CanBeNull] Action<AbpApplicationCreationOptions> optionsAction = null) where TStartupModule : IAbpModule { return AbpApplicationFactory.Create<TStartupModule>(services, optionsAction); } // ... 其他程式碼 }
在這個方法當中,通過名字WithExternalServiceProvider
我們就知道,這個 Applictaion 是依賴於外部的IServiceProvider
例項。
提示:
它繼承的AbpApplicationBase
基類還擁有另外一個實現,即AbpApplicationWithInternalServiceProvider
型別,該型別一般用於控制檯程式
,它會在 Abp vNext 框架內自行構建一個IServiceProvider
物件。
我們回到之前的程式碼,在這個AbpApplicationWithExternalServiceProvider
型別內部的構造方法很簡單,只是通過IServiceCollection
物件把自己注入到了服務集合當中。
internal class AbpApplicationWithExternalServiceProvider : AbpApplicationBase, IAbpApplicationWithExternalServiceProvider { public AbpApplicationWithExternalServiceProvider( [NotNull] Type startupModuleType, [NotNull] IServiceCollection services, [CanBeNull] Action<AbpApplicationCreationOptions> optionsAction ) : base( startupModuleType, services, optionsAction) { // 注入自己到 IoC 當中。 services.AddSingleton<IAbpApplicationWithExternalServiceProvider>(this); } // 執行框架初始化操作,主要工作是載入模組並執行其初始化方法。 public void Initialize(IServiceProvider serviceProvider) { Check.NotNull(serviceProvider, nameof(serviceProvider)); SetServiceProvider(serviceProvider); InitializeModules(); } }
重點程式碼在於它的基類建構函式,在基類建構函式當中 Abp vNext 注入了諸多 ASP.NET Core 需要的日誌服務、本地化服務等。並且它也抽象出了一個IModuleLoader
,用於輔助我們載入模組。
internal AbpApplicationBase( [NotNull] Type startupModuleType, [NotNull] IServiceCollection services, [CanBeNull] Action<AbpApplicationCreationOptions> optionsAction) { Check.NotNull(startupModuleType, nameof(startupModuleType)); Check.NotNull(services, nameof(services)); // 設定啟動模組。 StartupModuleType = startupModuleType; Services = services; // 新增一個空的物件訪問器,該訪問器的值會在初始化的時候被賦值。 services.TryAddObjectAccessor<IServiceProvider>(); // 呼叫使用者傳入的配置委託。 var options = new AbpApplicationCreationOptions(services); optionsAction?.Invoke(options); // 註冊自己。 services.AddSingleton<IAbpApplication>(this); services.AddSingleton<IModuleContainer>(this); // 新增日誌等基礎設施元件。 services.AddCoreServices(); // 新增核心的 Abp 服務,主要是模組系統相關元件。 services.AddCoreAbpServices(this, options); // 載入模組,並按照依賴關係排序,依次執行他們的生命週期方法。 Modules = LoadModules(services, options); }
提示:
這裡的物件訪問器其實就是一個佔位的型別物件,這樣方面後面替換其具體實現。例如在上文當中的IServiceProvider
通過ObjectAccessor<T>
物件包裹起來,其值是 NULL,但是在後面我們可以根據自己的需要替換其具體的 Value 。
2.3 替換 IoC 容器
再回到之前呼叫AddApplication<T>()
傳遞的委託方法,在其內部我們呼叫了UseAutofac()
方法。這個方法很簡單,內部就只有三行程式碼。
這三行程式碼主要是初始化了一個 AutoFac 的容器構建物件,其次注入IServiceProviderFactory
和 Abp 的預設實現AbpAutofacServiceProviderFactory
。
public static void UseAutofac(this AbpApplicationCreationOptions options) { var builder = new ContainerBuilder(); options.Services.AddObjectAccessor(builder); // 這裡是例項註冊。 options.Services.AddSingleton((IServiceProviderFactory<ContainerBuilder>) new AbpAutofacServiceProviderFactory(builder)); }
這個工廠類的就是在構建IServiceProvider
的時候使用,即BuildServiceProviderFromFactory()
方法。該方法內部邏輯很簡單,就是從已經註冊的服務集合(IServiceCollection
)當中獲得之前註冊的工廠類,通過呼叫工廠類的CreateServiceProvider()
方法構建IServiceProvider
,並作為返回值替換掉預設的 IoC 容器。
public static IServiceProvider BuildServiceProviderFromFactory([NotNull] this IServiceCollection services) { Check.NotNull(services, nameof(services)); // 遍歷已經註冊的型別,找到之前注入的工廠類。 foreach (var service in services) { var factoryInterface = service.ImplementationInstance?.GetType() .GetTypeInfo() .GetInterfaces() .FirstOrDefault(i => i.GetTypeInfo().IsGenericType && i.GetGenericTypeDefinition() == typeof(IServiceProviderFactory<>)); if (factoryInterface == null) { continue; } // 通過反射呼叫 IServiceProvider 的構建方法。 var containerBuilderType = factoryInterface.GenericTypeArguments[0]; return (IServiceProvider)typeof(ServiceCollectionCommonExtensions) .GetTypeInfo() .GetMethods() .Single(m => m.Name == nameof(BuildServiceProviderFromFactory) && m.IsGenericMethod) .MakeGenericMethod(containerBuilderType) .Invoke(null, new object[] { services, null }); } return services.BuildServiceProvider(); } // 這裡是另外一個過載方法的定義。 public static IServiceProvider BuildServiceProviderFromFactory<TContainerBuilder>([NotNull] this IServiceCollection services, Action<TContainerBuilder> builderAction = null) { Check.NotNull(services, nameof(services)); var serviceProviderFactory = services.GetSingletonInstanceOrNull<IServiceProviderFactory<TContainerBuilder>>(); if (serviceProviderFactory == null) { throw new AbpException($"Could not find {typeof(IServiceProviderFactory<TContainerBuilder>).FullName} in {services}."); } var builder = serviceProviderFactory.CreateBuilder(services); builderAction?.Invoke(builder); return serviceProviderFactory.CreateServiceProvider(builder); }
2.3 初始化 Abp 框架
這裡針對IApplicationBuilder
的擴充套件是在模組包Volo.Abp.AspNetCore
當中的,這裡僅講解 ASP.NET Core Mvc 專案是如何處理的。
public static void InitializeApplication([NotNull] this IApplicationBuilder app) { Check.NotNull(app, nameof(app)); // 獲取 IApplicationBuilde 的物件訪問器,並將其值設定為 app。 app.ApplicationServices.GetRequiredService<ObjectAccessor<IApplicationBuilder>>().Value = app; // 獲得之前在 ConfigureService 註冊的 Provider 型別,並呼叫其初始化方法。 app.ApplicationServices.GetRequiredService<IAbpApplicationWithExternalServiceProvider>().Initialize(app.ApplicationServices); }
這裡可能會疑惑ObjectAccessor<IApplicationBuilder>
是在什麼時候注入的,其實該型別是在AbpAspNetCoreModule
模組註冊的。
public class AbpAspNetCoreModule : AbpModule { // ... 其他程式碼 public override void ConfigureServices(ServiceConfigurationContext context) { // ... 其他程式碼 context.Services.AddObjectAccessor<IApplicationBuilder>(); } // ... 其他程式碼 }
接著看初始化方法內部的操作,初始化方法定義是在基類當中,方法名是InitializeModules()
,在方法內部,通過IModuleManager
來執行模組的初始化方法。
protected virtual void InitializeModules() { using (var scope = ServiceProvider.CreateScope()) { scope.ServiceProvider .GetRequiredService<IModuleManager>() .InitializeModules(new ApplicationInitializationContext(scope.ServiceProvider)); } }
除了模組的初始化,模組的銷燬動作 Abp vNext 好像是沒有作處理,你可以掛載IApplicationLifetime.ApplicationStopping
事件來手動執行模組的銷燬方法。
三、總結
總體來說 Abp vNext 的啟動流程與之前精簡了許多,這是因為在新的框架當中將許多基礎元件從核心層移除了,使用者可以自由選擇自己需要載入的元件。IoC 相關的程式碼則是通過的 Microsoft Dependency 提供的IServiceProvider
/IServiceCollection
進行操作,沒有了之前的IocManager
。