[Abp vNext 源码分析] - 2. 模块系统的变化
一、簡要說明
本篇文章主要分析 Abp vNext 當中的模塊系統(tǒng),從類型構(gòu)造層面上來看,Abp vNext 當中不再只是單純的通過?AbpModuleManager?來管理其他的模塊,它現(xiàn)在則是?IModuleManager?和?IModuleLoader 來協(xié)同工作,其他的代碼邏輯并無太大變化。
Abp vNext 規(guī)定每個模塊必須繼承自?IAbpModule 接口,這樣 vNext 系統(tǒng)在啟動的時候才會掃描到相應的模塊。與原來 Abp 框架一樣,每個模塊可以通過 DependsOnAttribute 特性來確定依賴關(guān)系,算法還是使用拓撲排序算法,來根據(jù)依賴性確定模塊的加載順序。(從最頂層的模塊,依次加載,直到啟動模塊。)
以我們的 Demo 項目為例,這里通過拓撲排序之后的依賴關(guān)系如上圖,這樣最開始執(zhí)行的即?AbpDataModule?模塊,然后再是?AbpAuditingModule?以此類推,直到我們的啟動模塊?DemoAppModule。
在 Abp vNext 當中,所有的組件庫/第三方庫都是以模塊的形式呈現(xiàn)的,模塊負責管理整個庫的生命周期,包括注冊組件,配置組件,銷毀組件等。
在最開始的 Abp 框架當中,一個模塊有 4 個生命周期,它們都是在抽象基類?AbpModule?當中定義的,分別是?預加載、初始化、初始化完成、銷毀。前三個生命周期是依次執(zhí)行的 預加載->初始化->初始化完成,而最后一個銷毀動作則是在程序終止的時候,通過 AbpModuleManager?遍歷模塊,調(diào)用其?ShutDown() 方法進行銷毀動作。
新的 Abp vNext 框架除了原有的四個生命周期以外,還抽象出了?IOnPreApplicationInitialization、IOnApplicationInitialization、IOnPostApplicationInitialization、IOnApplicationShutdown。從名字就可以看出來,新的四個生命周期是基于應用程序級別的,而不是模塊級別。
這是什么意思呢?在 Abp vNext 框架當中,模塊按照功能用途劃分為兩種類型的模塊。第一種是 框架模塊,它是框架的核心模塊,比如緩存、EF Core 等基礎設施就屬于框架模塊,其模塊的邏輯與處理基本都在傳統(tǒng)的三個生命周期進行處理。
在我們的?services.AddApplication()?階段就已經(jīng)完成所有初始化,可以給?應用程序模塊 提供服務。
第二種則是?應用程序模塊,這種模塊則是實現(xiàn)了特定的業(yè)務/功能,例如身份管理、租戶管理等,而新增加的四個生命周期基本是為這種類型的模塊服務的。
在代碼和結(jié)構(gòu)上來說,兩者并沒有區(qū)別,在這里僅僅是按用途進行了一次分類。單就模塊系統(tǒng)來說,其基本的作用就類似于一個配置類,配置某種組件的各種參數(shù)和一些默認邏輯。
二、源碼分析
2.1 模塊系統(tǒng)的基礎設施
模塊的初始化動作是在?AbpApplicationBase 基類開始的,在該基類當中除了注入模塊相關(guān)的基礎設施以外。還定義了模塊的初始化方法,即 LoadModules()?方法,在該方法內(nèi)部是調(diào)用的?IModuleLoader 去執(zhí)行具體的加載操作。
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();
services.AddCoreAbpServices(this, options);
Modules = LoadModules(services, options);
}
private IReadOnlyList<IAbpModuleDescriptor> LoadModules(IServiceCollection services, AbpApplicationCreationOptions options)
{
return services
.GetSingletonInstance<IModuleLoader>()
.LoadModules(
services,
StartupModuleType,
options.PlugInSources
);
}
2.2 模塊的初始化
進入?IModuleLoader?的默認實現(xiàn)?ModuleLoader,在它的?LoadModules() 方法中,基本邏輯如下:
掃描當前應用程序的所有模塊類,并構(gòu)建模塊描述對象。
基于模塊描述對象,使用拓撲排序算法來按照模塊的依賴性進行排序。
排序完成之后,遍歷排序完成的模塊描述對象,依次執(zhí)行它們的三個生命周期方法。
IServiceCollection services,
Type startupModuleType,
PlugInSourceList plugInSources)
{
Check.NotNull(services, nameof(services));
Check.NotNull(startupModuleType, nameof(startupModuleType));
Check.NotNull(plugInSources, nameof(plugInSources));
var modules = GetDescriptors(services, startupModuleType, plugInSources);
modules = SortByDependency(modules, startupModuleType);
ConfigureServices(modules, services);
return modules.ToArray();
}
在搜索模塊類型的時候,是使用的?AbpModuleHelper?工具類提供的?.FindAllModuleTypes() 方法。該方法會將我們的啟動模塊傳入,根據(jù)模塊上面的 DependsOn()?標簽遞歸構(gòu)建?模塊描述對象 的集合。
private List<IAbpModuleDescriptor> GetDescriptors(IServiceCollection services,
Type startupModuleType,
PlugInSourceList plugInSources)
{
var modules = new List<AbpModuleDescriptor>();
FillModules(modules, services, startupModuleType, plugInSources);
SetDependencies(modules);
return modules.Cast<IAbpModuleDescriptor>().ToList();
}
protected virtual void FillModules(
List<AbpModuleDescriptor> modules,
IServiceCollection services,
Type startupModuleType,
PlugInSourceList plugInSources)
{
foreach (var moduleType in AbpModuleHelper.FindAllModuleTypes(startupModuleType))
{
modules.Add(CreateModuleDescriptor(services, moduleType));
}
}
走進?AbpModuleHelper?靜態(tài)類,其代碼與結(jié)構(gòu)與原有的 Abp 框架類似,首先看下它的?FindAllModuleTypes() 方法,根據(jù)啟動模塊的類型遞歸查找所有的模塊類型,并添加到一個集合當中。
public static List<Type> FindAllModuleTypes(Type startupModuleType){
var moduleTypes = new List<Type>();
AddModuleAndDependenciesResursively(moduleTypes, startupModuleType);
return moduleTypes;
}
private static void AddModuleAndDependenciesResursively(List<Type> moduleTypes, Type moduleType)
{
AbpModule.CheckAbpModuleType(moduleType);
if (moduleTypes.Contains(moduleType))
{
return;
}
moduleTypes.Add(moduleType);
foreach (var dependedModuleType in FindDependedModuleTypes(moduleType))
{
AddModuleAndDependenciesResursively(moduleTypes, dependedModuleType);
}
}
public static List<Type> FindDependedModuleTypes(Type moduleType)
{
AbpModule.CheckAbpModuleType(moduleType);
var dependencies = new List<Type>();
var dependencyDescriptors = moduleType
.GetCustomAttributes()
.OfType<IDependedTypesProvider>();
foreach (var descriptor in dependencyDescriptors)
{
foreach (var dependedModuleType in descriptor.GetDependedTypes())
{
dependencies.AddIfNotContains(dependedModuleType);
}
}
return dependencies;
}
以上操作完成之后,我們就能獲得一個平級的模塊描述對象集合,我們?nèi)绻褂猛負渑判騺碇匦箩槍@個集合進行排序,就需要知道每個模塊的依賴項,根據(jù)?IAbpModuleDescriptor?的定義,我們可以看到它有一個?Dependencies 集合來存儲它的依賴項。
public interface IAbpModuleDescriptor{
Type Type { get; }
Assembly Assembly { get; }
IAbpModule Instance { get; }
bool IsLoadedAsPlugIn { get; }
IReadOnlyList<IAbpModuleDescriptor> Dependencies { get; }
}
而?SetDependencies(List<AbpModuleDescriptor> modules) 方法就是來設置每個模塊的依賴項的,代碼邏輯很簡單。遍歷之前的平級模塊描述對象集合,根據(jù)當前模塊的類型定義,找到其依賴項的類型定義。根據(jù)這個類型定義去平級的模塊描述對象集合搜索,將搜索到的結(jié)果存儲到當前的模塊描述對象中的 Dependencies 屬性當中。
protected virtual void SetDependencies(List<AbpModuleDescriptor> modules){
foreach (var module in modules)
{
SetDependencies(modules, module);
}
}
protected virtual void SetDependencies(List<AbpModuleDescriptor> modules, AbpModuleDescriptor module)
{
foreach (var dependedModuleType in AbpModuleHelper.FindDependedModuleTypes(module.Type))
{
var dependedModule = modules.FirstOrDefault(m => m.Type == dependedModuleType);
if (dependedModule == null)
{
throw new AbpException("Could not find a depended module " + dependedModuleType.AssemblyQualifiedName + " for " + module.Type.AssemblyQualifiedName);
}
module.AddDependency(dependedModule);
}
}
最后的拓撲排序就不在贅述,關(guān)于拓撲排序的算法,可以在我的?這篇 博文當中找到。
關(guān)于模塊的最后操作,就是執(zhí)行模塊的三個生命周期方法了,這塊代碼在?ConfigureServices() 方法當中,沒什么特別的的處理,遍歷整個模塊描述對象集合,依次執(zhí)行幾個方法就完了。
只是在這里的生命周期方法與之前的不一樣了,這里會為每個方法傳入一個服務上下文對象,主要是可以通過?IServiceCollection?來配置各個模塊的參數(shù),而不是原來的?Configuration 屬性。
protected virtual void ConfigureServices(List<IAbpModuleDescriptor> modules, IServiceCollection services){
var context = new ServiceConfigurationContext(services);
services.AddSingleton(context);
foreach (var module in modules)
{
if (module.Instance is AbpModule abpModule)
{
abpModule.ServiceConfigurationContext = context;
}
}
foreach (var module in modules.Where(m => m.Instance is IPreConfigureServices))
{
((IPreConfigureServices)module.Instance).PreConfigureServices(context);
}
foreach (var module in modules)
{
if (module.Instance is AbpModule abpModule)
{
if (!abpModule.SkipAutoServiceRegistration)
{
services.AddAssembly(module.Type.Assembly);
}
}
module.Instance.ConfigureServices(context);
}
foreach (var module in modules.Where(m => m.Instance is IPostConfigureServices))
{
((IPostConfigureServices)module.Instance).PostConfigureServices(context);
}
foreach (var module in modules)
{
if (module.Instance is AbpModule abpModule)
{
abpModule.ServiceConfigurationContext = null;
}
}
}
以上動作都是在?Startup?類當中的?ConfigureService() 方法中執(zhí)行,你可能會奇怪,剩下的四個應用程序生命周期的方法在哪兒執(zhí)行的呢?
這幾個方法是被抽象成了?IModuleLifecycleContributor?類型,在前面的?AddCoreAbpService()方法的內(nèi)部就被添加到了配置項里面。
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>();
});
}
執(zhí)行的話,則是在?Startup?類的?Configure()?方法當中,它會調(diào)用?AbpApplicationBase?基類的?InitializeModules()?方法,在該方法內(nèi)部也是遍歷所有的?Contributor (生命周期),再將所有的模塊對應的方法調(diào)用一次而已。
public void InitializeModules(ApplicationInitializationContext context){
LogListOfModules();
foreach (var Contributor in _lifecycleContributors)
{
foreach (var module in _moduleContainer.Modules)
{
Contributor.Initialize(context, module.Instance);
}
}
_logger.LogInformation("Initialized all modules.");
}
這里操作可能有點看不懂,不是說調(diào)用模塊的生命周期方法么,為啥還將實例傳遞給 Contributor 呢?我們找到一個 Contributor 的定義就知道了。
public class OnApplicationInitializationModuleLifecycleContributor : ModuleLifecycleContributorBase{
public override void Initialize(ApplicationInitializationContext context, IAbpModule module)
{
(module as IOnApplicationInitialization)?.OnApplicationInitialization(context);
}
}
這里我認為 Abp vNext 把 Contributor 抽象出來可能是為了后面方便擴展吧,如果你也有自己的看法不妨在評論區(qū)留言。
三、總結(jié)
至此,整個模塊系統(tǒng)的解析就結(jié)束了,如果看過 Abp 框架源碼解析的朋友就可以很明顯的感覺到,新框架的模塊系統(tǒng)除了生命周期多了幾個以外,其他的變化很少,基本沒太大的變化。
在 Abp vNext 框架里面,模塊系統(tǒng)是整個框架的基石,了解了模塊系統(tǒng)以后,對于剩下的設計就很好理解了。
原文地址:https://www.cnblogs.com/myzony/p/10734357.html
.NET社區(qū)新聞,深度好文,歡迎訪問公眾號文章匯總?http://www.csharpkit.com?
總結(jié)
以上是生活随笔為你收集整理的[Abp vNext 源码分析] - 2. 模块系统的变化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .NET Core 迁移躺坑记
- 下一篇: Asp.Net Core Docker镜