ABP模块系统

目录

模块系统简介

ABP 为创建模块及组织它们提供基础框架。一个模块可依赖于另一个模块。通常地,一个程序集做为一个模块。如果你的应用是多个程序集,建议为每个程序集定义一个模块。

模块的本质就是可重用性,你可以在任意的地方去调用,而且通过实现模块,你写的模块也可以给别人用。

200941480_f1c84fd3-d726-4e8f-a5e9-c50664c3a387

模块定义

一个模块定义成一个继承于 AppModule 类的类。 假设我们正在开发一个能用于不同应用的 Blog 模块,最简单的模块定义可能像如下所示:

public class MyBlogApplicationModule : AbpModule
{
    public override void Initialize()
    {
        IocManager.RegisterAssemblyByConvention(
Assembly.GetExecutingAssembly());
    }
}

模块定义类负责把自己的类注册到依赖注入里(约定做法如上所示)、配置应用和其它模块、为应用添加新的特性等…

生命周期方法

ABP 在应用启动和关闭时,为模块调用一些特定的方法。你可以重写这些方法来执行一些特定的任务。

ABP 按依赖关系顺序调用这些方法。如果模块 A 依赖于模块 B,模块 B 会在模块 A 前初始化,确切的启动方法顺序是:预初始化 B,预初始化 A,初始化 B,提交 B 和提交 A。这就是依赖路线。Shutdown 方法类似,不过顺序是相反的。

PreInitialize(预初始化)

应用启动时最先调用这个方法,它通常在初始化前将模块内的配置信息添加到框架或是处理其它模块所需执行的操作。

也可以在这里写一些特定的代码运行在依赖注入注册之前。例如,你想创建一个约定注册(Conventional Registration)类,你应该在这里使用 IocManager.AddConventionalRegisterer 方法来注册这个类。

Initialize(初始化)

依赖注入注册一般都在这里完成,一般都使用 IocManager.RegisterAssemblyByConvention 方法。如果想自定义依赖注册,可以依照 ABP 提供的其他方式完成注册(手动注册),如使用 IocManager.Register 方法直接注册或是Castle Windsor 注册.

PostInitialize(提交初始化)

在启动过程中最后调用这个方法,此时可以安全的解析依赖。

Shutdown(关闭)

当应用关闭时调用这个方法。

模块依赖

一个模块可以依赖于其它模块,它要求使用DenpendsOn特性显式地声明依赖关系,如下:

[DependsOn(typeof(MyBlogCoreModule))]
public class MyBlogApplicationModule : AbpModule
{
    public override void Initialize()
    {
        IocManager.RegisterAssemblyByConvention(
Assembly.GetExecutingAssembly());
    }
}

由于我们声明 MyBlogApplicationModule 依赖于 MyBlogCoreModule,所以 MyBlogCoreModule 会在 MyBlogApplicationModule 前初始化。 ABP 可以从 startup 模块解析依赖并按顺序初始化它们。startup 模块作为层次最浅的,最后初始化模块

调用链路

如存在三个模块,模块 A 作为系统的启动模块,依赖着模块 B,而模块 B 依赖着模块 C,则在启动时,会依照这个依赖关系,从最深层模块开始解析,如同解析依赖注入一般。

200943520_02a54cd5-84ba-4200-b98d-ad7da9882233 可对模块系统提供的三个生命周期方法断点跟踪,可以得到如下的调用顺序。从深层次模块开始到浅层模块,先调用 PreInitialize,全部调用完毕再回到深层次模块调用 Initialize,最后再次回到深层次调用 PostInitialize,全部执行完毕,模块加载的工作完成。

200944722_4562b4a5-8d31-45f0-9daa-ea5836e5d67b 该部分调用关系也可以直接翻阅源码

public virtual void StartModules()
{
    var sortedModules = _modules.GetSortedModuleListByDependency();
    sortedModules.ForEach(module => module.Instance.PreInitialize());
    sortedModules.ForEach(module => module.Instance.Initialize());
    sortedModules.ForEach(module => module.Instance.PostInitialize());
}

插件模块

虽然从启动模块开始通过依赖项加载模块,但ABP也可以动态加载模块,AbpBootstrapper类定义了PlugInSources属性,该属性可用于添加资源以动态加载插件模块。

插件源可以是实现IPlugInSource接口的任何类,PlugInFolderSource类实现从文件夹中的程序集获取插件模块。

在 Abp.AspNetCoreNuget 包中,提供了AddAbp 扩展方法,可用于实现在 Startup 类中添加插件源。

services.AddAbp<MyStartupModule>(options =>
{
    options.PlugInSources.Add(new FolderPlugInSource(@"C:\MyPlugIns"));
});

也可以调用 AddFolder扩展方法来从文件夹中的程序集中获取模块。

services.AddAbp<MyStartupModule>(options =>
{
    options.PlugInSources.AddFolder(@"C:\MyPlugIns");
});

附加程序集

IAssemblyFinder和ITypeFinder的默认实现,这两个接口在ABP中用于去识别特定类,仅在给定的程序集中查找模块所处的程序集和类型。可以通过重写模块中的GetAdditionalAssemblies方法去包含其他程序集,如获取非模块的程序集,将其加入到应用,应用在项目本身无需关联这个非模块的程序集。

自定义模块方法

模块中也可以有一些被其他依赖模块所使用的自定义方法。假设MyModule2依赖于MyModule1并想要在PreInitialize 方法中调用MyModule1的方法:

public class MyModule1 : AbpModule
{
    public override void Initialize()
    {
        IocManager.RegisterAssemblyByConvention(
        Assembly.GetExecutingAssembly());
    }
    public void MyModuleMethod1()
    {
        //this is a custom method of this module
    }
}
[DependsOn(typeof(MyModule1))]
public class MyModule2 : AbpModule
{
    private readonly MyModule1 _myModule1;
    public MyModule2(MyModule1 myModule1)
    {
        _myModule1 = myModule1;
    }
    public override void PreInitialize()
    {
        _myModule1.MyModuleMethod1(); //Call MyModule1's method
    }
    public override void Initialize()
    {
        IocManager.RegisterAssemblyByConvention(
        Assembly.GetExecutingAssembly());
    }
}

这里我们将构造函数注入MyModule1到MyModule2,因此MyModule2可以调用MyModule1的自定义方法。仅当Module2依赖于Module1时才可以这样做。

模块配置

虽然可以使用自定义模块方法配置模块,但建议使用配置系统来定义和设置模块的配置。

模块生命周期

模块类被自动注册为单例模式。

https://aspnetboilerplate.com/Pages/Documents/Module-System

2020-10-18,望技术有成后能回来看见自己的脚步