歡迎光臨
每天分享高質量文章

[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)
    {
        
        services.AddApplication(options =>
                                               {
                                                   options.UseAutofac();
                                               });
        
        
        return services.BuildServiceProviderFromFactory();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        
        app.InitializeApplication();
    }
}

在上面我們可以看到,ABP vNext 在註入服務的時候支援傳入一個 Action 委託。上述程式碼中,這個委託內部使用了 UseAutoFac() 將 AutoFac 的容器註入到了 MS IoC 當中,關於這塊程式碼下文會著重講解。

2.2 Abp 服務註冊

在上一節看到的服務註冊程式碼,是透過擴充套件 IServiceCollection 介面編寫的一個擴充套件方法實現的,在方法內部是透過 AbpApplicationFactory 靜態工廠來建立一個 AbpApplicationBase 實體。

public static class ServiceCollectionApplicationExtensions
{
    public static IAbpApplicationWithExternalServiceProvider AddApplication(
        [NotNull] this IServiceCollection services,
        [CanBeNull] Action optionsAction = null)
        where TStartupModule : IAbpModule
    {
        return AbpApplicationFactory.Create(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 optionsAction
        ) : base(
            startupModuleType,
            services,
            optionsAction)
    {
        
        services.AddSingleton(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 optionsAction)
{
    Check.NotNull(startupModuleType, nameof(startupModuleType));
    Check.NotNull(services, nameof(services));

    
    StartupModuleType = startupModuleType;
    Services = services;

    
    services.TryAddObjectAccessor();

    
    var options = new AbpApplicationCreationOptions(services);
    optionsAction?.Invoke(options);

    
    services.AddSingleton(this);
    services.AddSingleton(this);

    
    services.AddCoreServices();
    
    services.AddCoreAbpServices(this, options);

    
    Modules = LoadModules(services, options);
}

提示:

這裡的物件訪問器其實就是一個佔位的型別物件,這樣方面後面替換其具體實現。例如在上文當中的 IServiceProvider 透過 ObjectAccessor 物件包裹起來,其值是 NULL,但是在後面我們可以根據自己的需要替換其具體的 Value 。

2.3 替換 IoC 容器

再回到之前呼叫 AddApplication() 傳遞的委託方法,在其內部我們呼叫了 UseAutofac() 方法。這個方法很簡單,內部就只有三行程式碼。

這三行程式碼主要是初始化了一個 AutoFac 的容器構建物件,其次註入 IServiceProviderFactory 和 Abp 的預設實現 AbpAutofacServiceProviderFactory

public static void UseAutofac(this AbpApplicationCreationOptions options)
{
    var builder = new ContainerBuilder();
    options.Services.AddObjectAccessor(builder);
    
    options.Services.AddSingleton((IServiceProviderFactory) 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;
        }

        
        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([NotNull] this IServiceCollection services, Action builderAction = null)
{
    Check.NotNull(services, nameof(services));

    var serviceProviderFactory = services.GetSingletonInstanceOrNull>();
    if (serviceProviderFactory == null)
    {
        throw new AbpException($"Could not find {typeof(IServiceProviderFactory).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));

    
    app.ApplicationServices.GetRequiredService>().Value = app;
    
    
    app.ApplicationServices.GetRequiredService().Initialize(app.ApplicationServices);
}

這裡可能會疑惑 ObjectAccessor 是在什麼時候註入的,其實該型別是在 AbpAspNetCoreModule 模組註冊的。

public class AbpAspNetCoreModule : AbpModule
{
    
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        
        context.Services.AddObjectAccessor();
    }
    
}

接著看初始化方法內部的操作,初始化方法定義是在基類當中,方法名是 InitializeModules() ,在方法內部,透過 IModuleManager 來執行模組的初始化方法。

protected virtual void InitializeModules()
{
    using (var scope = ServiceProvider.CreateScope())
    {
        scope.ServiceProvider
            .GetRequiredService()
            .InitializeModules(new ApplicationInitializationContext(scope.ServiceProvider));
    }
}

除了模組的初始化,模組的銷毀動作 Abp vNext 好像是沒有作處理,你可以掛載 IApplicationLifetime.ApplicationStopping 事件來手動執行模組的銷毀方法。

三、總結

總體來說 Abp vNext 的啟動流程與之前精簡了許多,這是因為在新的框架當中將許多基礎元件從核心層移除了,使用者可以自由選擇自己需要載入的元件。IoC 相關的程式碼則是透過的 Microsoft Dependency 提供的 IServiceProvider/IServiceCollection 進行操作,沒有了之前的 IocManager

已同步到看一看
贊(0)

分享創造快樂