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

来源:https://www.cnblogs.com/myzony/archive/2019/04/17/10722480.html
-Advertisement-
Play Games

一、簡要說明 本篇文章主要剖析與講解 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

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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這個例子是基於客戶端與webapi進行進行交互的身份認證,當然也適用於其他情況下的身份認證。 簡單的交互過程: 1.首先輸入用戶名、密碼進行登錄操作 2.伺服器驗證用戶名、密碼的正確性,驗證通過之後,伺服器對一個json字元串進行加密,加密的內容、加密方法可以自己確定。 本次我加密的內容主要是用戶名 ...
  • Unity 3D本來是由德國的一些蘋果粉絲開發的一款游戲引擎,一直只能用於Mac平臺,所以一直不被業外人士所知曉。但是後來也推出了2.5版,同時發佈了PC版本,並將其發佈方向拓展到手持移動設備。Unity 3D游戲開發學習路線(方法篇)分享給大家。怎麼學Unity 3D游戲開發?要瞭解U3D最重要的 ...
  • 事件(Event)例如:最近的視覺中國‘黑洞事件’。我們大多數人(訂閱者)是通過XX平臺(發佈者)得知的這一消息,然後訂閱者A出售視覺中國的股票(觸發的方法),訂閱者B買入視覺中國的股票。 運行結果: ...
  • FastReport 交流群 群 號:554714044 前言 由於公司開發新產品,前後端分離.netcore +Angular ,之前C/S項目一直使用FastReport ,考慮到員工切換比較困難,而且最最重要的是BS版少了很多內容,例如合計,函數最常用的功能都沒有,所以B/S端列印控制項繼續沿用 ...
  • 之前說過,core需要什麼功能就添加並使用什麼中間件 照例,在Startup.cs的ConfigureServices方法中添加services.AddSession();再在Configure方法中添加app.UseSession();(註意要在UseMvc之前) 再引用Microsoft.Asp ...
  • 一.概述 在前二篇中講到了客戶端授權的二種方式: GrantTypes.ClientCredentials憑據授權和GrantTypes.ResourceOwnerPassword密碼授權,都是OAuth2.0協議。本篇使用OpenID Connect添加用戶認證,客戶端授權是GrantTypes. ...
  • 現在我們的類庫項目大多是 NETStandard2.0 項目,但是 netstandard2.0 只是一個標準介面定義,具體的實現還是要看實際運行的框架,如 netfx47 或 netcoreapp2.1,可能在大部分情況下,對外表現是一致的,但是,某一些 API 可能會不一致,這裡就開一篇文章收集... ...
  • 在上一篇文章《C#/.NET基於Topshelf創建Windows服務程式及服務的安裝和卸載》中,我們瞭解發C#/.NET創建基於Topshelf Windows服務程式的大致流程,參數配置以及服務的安裝和卸載。同時,我們也使用一個簡單的定時任務演示了Topshelf服務的執行情況。 今天我將繼續... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...