[Abp vNext 源碼分析] - 1. 框架啓動流程分析

1、簡要說明

本篇文章主要剖析與講解 Abp vNext 在 Web API 項目下的啓動流程,讓你們瞭解整個 Abp vNext 框架是如何運做的。總的來講 ,Abp vNext 比起 ABP 框架更加精簡。由於在 vNext 版本當中,原來歸屬於 Abp 庫的許多內置的基本組件 (組織單元、攔截器等) 被拆分紅了單獨的模塊,這樣咱們來看它整個啓動流程就更加地直觀清晰。html

2、源碼分析

要分析其源碼,我這裏是從他官方的 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 框架。app

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 實例。ide

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 對象把本身注入到了服務集合當中。ui

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,用於輔助咱們加載模塊。this

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 事件來手動執行模塊的銷燬方法。

3、總結

整體來講 Abp vNext 的啓動流程與以前精簡了許多,這是由於在新的框架當中將許多基礎組件從核心層移除了,用戶能夠自由選擇本身須要加載的組件。IoC 相關的代碼則是經過的 Microsoft Dependency 提供的 IServiceProvider/IServiceCollection 進行操做,沒有了以前的 IocManager

4、點擊我跳轉到文章目錄

相關文章
相關標籤/搜索