一、子產品化應用
1、繼承AbpModule
每個子產品都應該定義一個子產品類.定義子產品類的最簡單方法是建立一個派生自
AbpModule
的類,如下所示:
2、配置依賴注入和其他子產品---ConfigService方法
在ConfigService中注入你用到的所有服務
你可以按照Microsoft的文檔中的說明逐個注冊依賴項.但ABP有一個依照約定的依賴注冊系統,可以自動注冊程式集中的所有服務.有關依賴項注入系統的更多資訊.
你也可以通過這種方式配置其他服務和子產品.例:
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中引入子產品就可以了
二、子產品化原理
1、Abp vNext 規定每個子產品必須繼承自
IAbpModule
接口,這樣 vNext 系統在啟動的時候才會掃描到相應的子產品。與原來 Abp 架構一樣,每個子產品可以通過
DependsOnAttribute
特性來确定依賴關系,算法還是使用拓撲排序算法,來根據依賴性确定子產品的加載順序。(從最頂層的子產品,依次加載,直到啟動子產品。)
以我們的 Demo 項目為例,這裡通過拓撲排序之後的依賴關系如上圖,這樣最開始執行的即
AbpDataModule
子產品,然後再是
AbpAuditingModule
以此類推,直到我們的啟動子產品
DemoAppModule
。
在 Abp vNext 當中,所有的元件庫/第三方庫都是以子產品的形式呈現的,子產品負責管理整個庫的生命周期,包括注冊元件,配置元件,銷毀元件等。
在最開始的 Abp 架構當中,一個子產品有 4 個生命周期,它們都是在抽象基類
AbpModule
當中定義的,分别是 預加載、初始化、初始化完成、銷毀。前三個生命周期是依次執行的 預加載->初始化->初始化完成,而最後一個銷毀動作則是在程式終止的時候,通過
AbpModuleManager
周遊子產品,調用其
ShutDown()
三、子產品化源碼解讀
1、在子產品化應用部分我們看到子產品的啟動是以項目中Startup.cs如下的段代碼開始的,我們就從這裡開始進入子產品化的源碼解讀吧
public void ConfigureServices(IServiceCollection services)
{
services.AddApplication<FileManagementServiceHostModule>();
}
2、其實AddApplication<T>() 這個方法是一個擴充方法寫在類ServiceCollectionApplicationExtensions中,方法中泛型參數TStartupModule就是子產品的類型,可以看到泛型限制是要實作IAbpModule接口
,這就是我們自定義的子產品FileManagementServiceHostModule必須要繼承AbpModule的原因。
3、在這個方法中調用了AbpApplicationFactory類的CreateAsync方法,那我們現在進入這個類看看
此方法就是用我們自定義好的子產品FileManagementServiceHostModule建立出了一個實作了IAbpApplicationWithExternalServiceProvider的對象AbpApplicationWithExternalServiceProvider
4、那我們看看IAbpApplicationWithExternalServiceProvider
建構完成基本的實體後,調用Initialize方法初始化架構.再看看IAbpApplication接口
包含啟動子產品類型,DI注入集合、DI服務提供類,以及一個關閉應用程式必須執行的ShutDown方法.在看看IModuleContainer
包含子產品集合,在Abp中,子產品代表一個程式集.這裡就是啟動abp vnext架構的啟動子產品類型所依賴的所有子產品類型,即所有的程式集集合你可以這樣了解.因為一個Module類型(繼承AbpModule類型或者實作IAbpModule接口的類型)代表一個程式集.且一個程式集隻有一個Module類型(繼承AbpModule類型或者實作IAbpModule接口的類型).
5、以上分析可知,子產品的初始化是從IAbpApplication接口的實作類AbpApplicationBase開始的
在該基類當中除了注入子產品相關的基礎設施以外。還定義了子產品的初始化方法,即
LoadModules()
方法,在該方法内部是調用的
IModuleLoader
去執行具體的加載操作。
那我們先來詳細看看構造器中作了哪些事
先看看services.AddCoreServices();
進入此擴充方法發現注入配置檔案、日志、國際化等服務.接着看AddCoeAbpServices方法
在該方法中注入ModuleLoader(處理程式集間依賴關系,處理子產品加載生命周期、的核心類型)、程式集發現類(所有程式集都能通過該類型拿到,隻要程式集加入到了架構)、類型發現類(程式集集合所包含的所有類型)
還進行了配置檔案的初始化
再看看services.AddAssemblyOf<IAbpApplication>();是在做什麼
從DI中讀取程式集注冊規則類清單,如果沒有,則寫入預設的程式集注冊規則類
再看看接下來的代碼
此處是在向DI中預先寫入AbpModuleLifecycleOptions,該參數用于控制子產品加載的生命周期,這四個Contributor分别對應子產品加載生命周期的接口
讓我們再次回來AbpApplicationBase類的構造函數中中看看比較核心的一段代碼Modules = LoadModules(services, options);
作用是加載所有子產品。
進入其核心類ModuleLoader中檢視
到此,本子產品和它所依賴的所有的子產品都初始化完了,繼續往下面看
看看這個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.");
}