【ABP框架系列学习】模块系统(4)

0.引言

ABP提供了构建模块和经过组合模块以建立应用程序的基础设施。一个模块能够依赖于另一个模块。一般,程序集能够认为是模块。若是建立多个程序集的应用程序,建议为每一个程序集建立模块定义。安全

当前,模块系统主要集中在服务器,而不是客户端。服务器

1.模块定义

模块是从ABP包中的AbpModule派生的类定义的。好比说开发一个能够用于不一样应用程序的博客模块(Blog Module)。最简单的模块定义以下 :app

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

模块定义类负责经过依赖注入注册类,若有必要(能够像上述事例按惯例完成)。它还能够配置应用程序和其它模块,给应用程序增长新的功能等等。框架

2.生命周期方法

ABP在程序启动和关闭时调用模块一些特定的方法。你能够重写这些方法以执行某些特定的任务。ide

ABP按照依赖顺序调用这些方法。若是模块A依赖模块B,那么模块B在模块A以前初始化。函数

启动方法执行准确的顺序:PreInitialize-B, PreInitialize-A, Initialize-B, Initialize-A, PostInitialize-B, PostInitialize-A。对于全部依赖关系图都是如此。关闭方法也是相似的,但顺序相反。ui

相关源码:模块启动时依次执行PreInitialize()、Initialize()、PostInitialize(),模块关闭时首先Reverse()、而后在逐个模块Shutdown()。this

        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());
        }

        public virtual void ShutdownModules()
        {
            Logger.Debug("Shutting down has been started");

            var sortedModules = _modules.GetSortedModuleListByDependency();
            sortedModules.Reverse();
            sortedModules.ForEach(sm => sm.Instance.Shutdown());

            Logger.Debug("Shutting down completed.");
        }

PreInitialize

当应用程序启动时,首先调用该方法。它是框架和其它模块初始化以前配置它们的首选方法。spa

你还能够在该方法中编写特定的代码,以便在依赖注入注册以前运行。例如,若是你建立一个传统的注册类,那么你应在该方法中使用IOCManager.AddConventionalRegisterer方法注册它们。插件

Initialize

该方法是依赖注入注册的地方,经过使用IocManager.RegisterAssemblyByConvention方法完成注册。若是想定义自定义的依赖注册,请见后续依赖注入章节。

PostInitialize

该方法在程序启动的最后调用。在这里解析依赖是安全的。

Shutdown

该方法在程序关闭时调用。

3.模块依赖(Module Dependencies)

一个模块能够依赖于另外的模块。你能够经过DependsOn特性显示声明依赖项,以下代码:

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

上述事例代码中,声明了MyBlogApplicationModule模块依赖于MyBlogCoreModule模块,那么MyBlogCoreModule模块应该在MyBlogApplicationModule模块以前完成初始化。

ABP能够从启动模块(start module)开始就递归的解析依赖关系,并相应地初始化它们。启动模块(start module)是最后进行初始化的模块。

4.插件模块

虽然模块从启动模块开始查找并遍历依赖关系,ABP还能够动态加载模块。AbpBootstrapper类中定义了PlugInSources属性,该属性可用于向动态加载的插件模块添加源。插件源能够是实现IPlugInSource接口的任何类。经过实现FolderPlugInSource类以从指定文件夹中的程序集获取插件模块。

ASP.NET CORE

ABP中ASP.NET CORE模块在AddAbp扩展方法中定义选项,用于在启动类中添加插件源:

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

也可使用更简单的语法AddFolder扩展方法:

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

ASP.NET MVC,Web API

对于传统的ASP.NET MVC应用程序,能够经过重写global.asax文件中Application_Start方法添加插件文件夹,以下代码:

public class MvcApplication : AbpWebApplication<MyStartupModule>
{
    protected override void Application_Start(object sender, EventArgs e)
    {
        AbpBootstrapper.PlugInSources.AddFolder(@"C:\MyPlugIns");
        //...
        base.Application_Start(sender, e);
    }
}

Controllers in PlugIns

若是你的模块包括MVC或Web API Controolers,ASP.NET不能查找你的控制器。为了克服这个问题,你能够修改global.asax文件,以下代码:

using System.Web;
using Abp.PlugIns;
using Abp.Web;
using MyDemoApp.Web;

[assembly: PreApplicationStartMethod(typeof(PreStarter), "Start")]

namespace MyDemoApp.Web
{
    public class MvcApplication : AbpWebApplication<MyStartupModule>
    {
    }

    public static class PreStarter
    {
        public static void Start()
        {
            //...
            MvcApplication.AbpBootstrapper.PlugInSources.AddFolder(@"C:\MyPlugIns\");
            MvcApplication.AbpBootstrapper.PlugInSources.AddToBuildManager();
        }
    }
}

附加程序集(Additional Assemblies)

默认实现IAssemblyFinder和ITypeFinder接口只能在这些程序集中查找模块程序集和类型。也能够在模块中重写GetAdditionalAssembliesy方法来包括其它程序集。

自定义模块方法(Custom Module Methods)

你的模块还能够拥有自定义的方法,并能在依赖于这个模块的其它模块中调用这个方法。假设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中的自定义方法,前提是MyModule2依赖于MyModule1。

模块配置(Module Configuration)

然而自定义方法能够用来配置模块,建议使用启动配置(startup configuration)系统来定义和设置模块的配置。

模块生命周期(Module Lifetime)

模块类自动注册为单实例对象(singleton)。

相关文章
相关标签/搜索