一、簡要說明 本篇文章主要剖析與講解 Abp vNext 在 Web API 項目下的啟動流程,讓大家瞭解整個 Abp vNext 框架是如何運作的。總的來說 ,Abp vNext 比起 ABP 框架更加精簡。因為在 vNext 版本當中,原來歸屬於 Abp 庫的許多內置的基本組件 (組織單元、攔截 ...
一、簡要說明
本篇文章主要剖析與講解 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
。