天天看點

ABP vNext系列文章01---子產品化

一、子產品化應用

 1、繼承AbpModule

每個子產品都應該定義一個子產品類.定義子產品類的最簡單方法是建立一個派生自​

​AbpModule​

​的類,如下所示:

ABP vNext系列文章01---子產品化

 2、配置依賴注入和其他子產品---ConfigService方法

在ConfigService中注入你用到的所有服務

你可以按照Microsoft的​​文檔​​中的說明逐個注冊依賴項.但ABP有一個依照約定的依賴注冊系統,可以自動注冊程式集中的所有服務.有關依賴項注入系統的更多資訊.

ABP vNext系列文章01---子產品化

你也可以通過這種方式配置其他服務和子產品.例:

public override void ConfigureServices(ServiceConfigurationContext context)
    {
        //為應用程式配置預設的連接配接字元串
        Configure<AbpDbConnectionOptions>(options =>
        {
            options.ConnectionStrings.Default = "......";
        });
    }      

​AbpModule​

​​類還定義了​

​PreConfigureServices​

​​和​

​PostConfigureServices​

​​方法用來在​

​ConfigureServices​

​​之前或之後覆寫和編寫你的代碼.請注意,在這些方法中編寫的代碼将在所有其他子產品的​

​ConfigureServices​

​方法之前/之後執行.

3、配置中間件管道---OnApplicationInitialization方法

一旦配置了所有子產品的所有服務,應用程式就會通過初始化所有子產品來啟動.在此階段,你可以從​

​IServiceProvider​

​中擷取服務,因為這時它已準備就緒且可用.

public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        var myService = context.ServiceProvider.GetService<MyService>();
        myService.DoSomething();
    }      

​OnApplicationInitialization​

​通常由啟動子產品用于建構ASP.NET Core應用程式的中間件管道.例:

public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        var app = context.GetApplicationBuilder();
        var env = context.GetEnvironment();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseMvcWithDefaultRoute();
    }      

如果子產品需要,你還可以執行啟動邏輯

應用程式初始化前和後

​AbpModule​

​​類還定義了​

​OnPreApplicationInitialization​

​​和​

​OnPostApplicationInitialization​

​​方法用來在​

​OnApplicationInitialization​

​​之前或之後覆寫和編寫你的代碼.請注意,在這些方法中編寫的代碼将在所有其他子產品的​

​OnApplicationInitialization​

​方法之前/之後執行.

應用程式關閉

最後,如果要在應用程式關閉時執行某些代碼,你可以覆寫​

​OnApplicationShutdown​

​方法.

4、子產品依賴

在子產品化應用程式中,一個子產品依賴于另一個或幾個子產品并不罕見.如果一個Abp子產品依賴于另一個子產品,它必須聲明​

​[DependsOn]​

​特性,如下所示:

你可以根據需要使用多個​

​DependsOn​

​​特性或将多個子產品類型傳遞給單個​

​DependsOn​

​特性.

依賴子產品可能依賴于另一個子產品,但你隻需要定義直接依賴項.ABP在啟動時會調查應用程式的依賴關系,并以正确的順序初始化/關閉子產品.

 5、項目引入子產品

在項目啟動檔案 startup中引入子產品就可以了

ABP vNext系列文章01---子產品化

二、子產品化原理

1、Abp vNext 規定每個子產品必須繼承自 ​

​IAbpModule​

​​ 接口,這樣 vNext 系統在啟動的時候才會掃描到相應的子產品。與原來 Abp 架構一樣,每個子產品可以通過 ​

​DependsOnAttribute​

​ 特性來确定依賴關系,算法還是使用拓撲排序算法,來根據依賴性确定子產品的加載順序。(從最頂層的子產品,依次加載,直到啟動子產品。)

ABP vNext系列文章01---子產品化

以我們的 Demo 項目為例,這裡通過拓撲排序之後的依賴關系如上圖,這樣最開始執行的即 ​

​AbpDataModule​

​​ 子產品,然後再是 ​

​AbpAuditingModule​

​​ 以此類推,直到我們的啟動子產品 ​

​DemoAppModule​

​。

在 Abp vNext 當中,所有的元件庫/第三方庫都是以子產品的形式呈現的,子產品負責管理整個庫的生命周期,包括注冊元件,配置元件,銷毀元件等。

在最開始的 Abp 架構當中,一個子產品有 4 個生命周期,它們都是在抽象基類 ​

​AbpModule​

​ 當中定義的,分别是 預加載、初始化、初始化完成、銷毀。前三個生命周期是依次執行的 預加載->初始化->初始化完成,而最後一個銷毀動作則是在程式終止的時候,通過 ​

​AbpModuleManager​

​​ 周遊子產品,調用其 ​

​ShutDown()​

ABP vNext系列文章01---子產品化
ABP vNext系列文章01---子產品化

三、子產品化源碼解讀

1、在子產品化應用部分我們看到子產品的啟動是以項目中Startup.cs如下的段代碼開始的,我們就從這裡開始進入子產品化的源碼解讀吧

public void ConfigureServices(IServiceCollection services)
        {
            services.AddApplication<FileManagementServiceHostModule>();

          
        }      

2、其實AddApplication<T>() 這個方法是一個擴充方法寫在類ServiceCollectionApplicationExtensions中,方法中泛型參數TStartupModule就是子產品的類型,可以看到泛型限制是要實作IAbpModule接口 

,這就是我們自定義的子產品FileManagementServiceHostModule必須要繼承AbpModule的原因。

ABP vNext系列文章01---子產品化

3、在這個方法中調用了AbpApplicationFactory類的CreateAsync方法,那我們現在進入這個類看看

ABP vNext系列文章01---子產品化
ABP vNext系列文章01---子產品化

 此方法就是用我們自定義好的子產品FileManagementServiceHostModule建立出了一個實作了IAbpApplicationWithExternalServiceProvider的對象AbpApplicationWithExternalServiceProvider

4、那我們看看IAbpApplicationWithExternalServiceProvider

ABP vNext系列文章01---子產品化

建構完成基本的實體後,調用Initialize方法初始化架構.再看看IAbpApplication接口

ABP vNext系列文章01---子產品化

包含啟動子產品類型,DI注入集合、DI服務提供類,以及一個關閉應用程式必須執行的ShutDown方法.在看看IModuleContainer

ABP vNext系列文章01---子產品化

 包含子產品集合,在Abp中,子產品代表一個程式集.這裡就是啟動abp vnext架構的啟動子產品類型所依賴的所有子產品類型,即所有的程式集集合你可以這樣了解.因為一個Module類型(繼承AbpModule類型或者實作IAbpModule接口的類型)代表一個程式集.且一個程式集隻有一個Module類型(繼承AbpModule類型或者實作IAbpModule接口的類型).

 5、以上分析可知,子產品的初始化是從IAbpApplication接口的實作類AbpApplicationBase開始的

在該基類當中除了注入子產品相關的基礎設施以外。還定義了子產品的初始化方法,即 ​

​LoadModules()​

​​ 方法,在該方法内部是調用的 ​

​IModuleLoader​

​ 去執行具體的加載操作。

那我們先來詳細看看構造器中作了哪些事

ABP vNext系列文章01---子產品化

 先看看services.AddCoreServices();

ABP vNext系列文章01---子產品化

 進入此擴充方法發現注入配置檔案、日志、國際化等服務.接着看AddCoeAbpServices方法

ABP vNext系列文章01---子產品化

 在該方法中注入ModuleLoader(處理程式集間依賴關系,處理子產品加載生命周期、的核心類型)、程式集發現類(所有程式集都能通過該類型拿到,隻要程式集加入到了架構)、類型發現類(程式集集合所包含的所有類型)

ABP vNext系列文章01---子產品化

還進行了配置檔案的初始化

ABP vNext系列文章01---子產品化

 再看看services.AddAssemblyOf<IAbpApplication>();是在做什麼

ABP vNext系列文章01---子產品化

 從DI中讀取程式集注冊規則類清單,如果沒有,則寫入預設的程式集注冊規則類

 再看看接下來的代碼

ABP vNext系列文章01---子產品化

 此處是在向DI中預先寫入AbpModuleLifecycleOptions,該參數用于控制子產品加載的生命周期,這四個Contributor分别對應子產品加載生命周期的接口

讓我們再次回來AbpApplicationBase類的構造函數中中看看比較核心的一段代碼Modules = LoadModules(services, options);

作用是加載所有子產品。

進入其核心類ModuleLoader中檢視

ABP vNext系列文章01---子產品化

到此,本子產品和它所依賴的所有的子產品都初始化完了,繼續往下面看

ABP vNext系列文章01---子產品化

 看看這個configureServices()又在做什麼呢,進入此方法體中

public virtual void ConfigureServices()
    {
        CheckMultipleConfigureServices();
        // 構造一個服務上下文,并将其添加到 IoC 容器當中。
        var context = new ServiceConfigurationContext(Services);
        Services.AddSingleton(context);

        foreach (var module in Modules)
        {
            if (module.Instance is AbpModule abpModule)
            {
                abpModule.ServiceConfigurationContext = context;
            }
        }

        // 執行預加載方法 PreConfigureServices。
        foreach (var module in Modules.Where(m => m.Instance is IPreConfigureServices))
        {
            try
            {
                ((IPreConfigureServices)module.Instance).PreConfigureServices(context);
            }
            catch (Exception ex)
            {
                throw new AbpInitializationException($"An error occurred during {nameof(IPreConfigureServices.PreConfigureServices)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex);
            }
        }

        var assemblies = new HashSet<Assembly>();

        // 執行初始化方法 ConfigureServices。
        foreach (var module in Modules)
        {
            if (module.Instance is AbpModule abpModule)
            {
                if (!abpModule.SkipAutoServiceRegistration)
                {
                    var assembly = module.Type.Assembly;
                    if (!assemblies.Contains(assembly))
                    {
                        Services.AddAssembly(assembly);
                        assemblies.Add(assembly);
                    }
                }
            }

            try
            {
                module.Instance.ConfigureServices(context);
            }
            catch (Exception ex)
            {
                throw new AbpInitializationException($"An error occurred during {nameof(IAbpModule.ConfigureServices)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex);
            }
        }

        // 執行初始化完成方法 PostConfigureServices。
        foreach (var module in Modules.Where(m => m.Instance is IPostConfigureServices))
        {
            try
            {
                ((IPostConfigureServices)module.Instance).PostConfigureServices(context);
            }
            catch (Exception ex)
            {
                throw new AbpInitializationException($"An error occurred during {nameof(IPostConfigureServices.PostConfigureServices)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex);
            }
        }
        // 将服務上下文置為 NULL。
        foreach (var module in Modules)
        {
            if (module.Instance is AbpModule abpModule)
            {
                abpModule.ServiceConfigurationContext = null;
            }
        }

        _configuredServices = true;
    }      

以上動作都是在 ​

​Startup​

​​ 類當中的 ​

​ConfigureService()​

​ 方法中執行,你可能會奇怪,剩下的四個應用程式生命周期的方法在哪兒執行的呢?

這幾個方法是被抽象成了 ​

​IModuleLifecycleContributor​

​​ 類型,在前面的 ​

​AddCoreAbpService()​

​ 方法的内部就被添加到了配置項裡面。

internal static void AddCoreAbpServices(this IServiceCollection services,
    IAbpApplication abpApplication, 
    AbpApplicationCreationOptions applicationCreationOptions)
{
    // ... 其他代碼
    
    services.Configure<ModuleLifecycleOptions>(options =>
    {
        options.Contributors.Add<OnPreApplicationInitializationModuleLifecycleContributor>();
        options.Contributors.Add<OnApplicationInitializationModuleLifecycleContributor>();
        options.Contributors.Add<OnPostApplicationInitializationModuleLifecycleContributor>();
        options.Contributors.Add<OnApplicationShutdownModuleLifecycleContributor>();
    });
}      

執行的話,則是在 ​

​Startup​

​​ 類的 ​

​Configure()​

​​ 方法當中,它會調用 ​

​AbpApplicationBase​

​​ 基類的 ​

​InitializeModules()​

​​ 方法,在該方法内部也是周遊所有的 ​

​Contributor​

​ (生命周期),再将所有的子產品對應的方法調用一次而已。

public void InitializeModules(ApplicationInitializationContext context)
{
    LogListOfModules();

    // 周遊應用程式的幾個生命周期。
    foreach (var Contributor in _lifecycleContributors)
    {
        // 周遊所有的子產品,将子產品執行個體傳入具體的 Contributor,友善在其内部調用具體的生命周期方法。
        foreach (var module in _moduleContainer.Modules)
        {
            Contributor.Initialize(context, module.Instance);
        }
    }

    _logger.LogInformation("Initialized all modules.");
}      

繼續閱讀