一、模块化应用

 1、继承AbpModule

每个模块都应该定义一个模块类.定义模块类的最简单方法是创建一个派生自AbpModule的类,如下所示:

 2、配置依赖注入和其他模块---ConfigService方法

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

你可以按照Microsoft的文档中的说明逐个注册依赖项.但ABP有一个依照约定的依赖注册系统,可以自动注册程序集中的所有服务.有关依赖项注入系统的更多信息.

你也可以通过这种方式配置其他服务和模块.例:

public override void ConfigureServices(ServiceConfigurationContext context)
{
//为应用程序配置默认的连接字符串
Configure<AbpDbConnectionOptions>(options =>
{
options.ConnectionStrings.Default = "......";
});
}

AbpModule类还定义了PreConfigureServicesPostConfigureServices方法用来在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类还定义了OnPreApplicationInitializationOnPostApplicationInitialization方法用来在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.");
}

ABP vNext系列文章01---模块化的更多相关文章

  1. ABP vNext系列文章03---依赖注入

    一.依赖注入的类型注册 ABP的依赖注入系统是基于Microsoft的依赖注入扩展库(Microsoft.Extensions.DependencyInjection nuget包)开发的.因此,它的 ...

  2. 2019 年起如何开始学习 ABP 框架系列文章-开篇有益

    2019 年起如何开始学习 ABP 框架系列文章-开篇有益 [[TOC]] 本系列文章推荐阅读地址为:52ABP 开发文档 https://www.52abp.com/Wiki/52abp/lates ...

  3. Learn-JavaScript-with-MDN 系列文章: 01. var & let & const 对比

    Learn-JavaScript-with-MDN 系列文章: 01. var & let & const 对比 var & let & const 区别 https: ...

  4. 一步一步学习ABP项目系列文章目录

    1.概述 基于DDD的.NET开发框架 - ABP初探 基于DDD的.NET开发框架 - ABP分层设计 基于DDD的.NET开发框架 - ABP模块设计 基于DDD的.NET开发框架 - ABP启动 ...

  5. [Abp vNext 源码分析] - 7. 权限与验证

    一.简要说明 在上篇文章里面,我们在 ApplicationService 当中看到了权限检测代码,通过注入 IAuthorizationService 就可以实现权限检测.不过跳转到源码才发现,这个 ...

  6. [Abp vNext 入坑分享] - 8.Redis与Refit的接入

    前言 本章结束之后,这个abp vnext系列算是初步完结了,基础的组件都已经接入了.如果各位还需要其它的组件的话,可以自己按需要进行接入使用.其实这个只是一个基础的框架,可以自己根据需要进行变通的. ...

  7. Abp vNext 番外篇-疑难杂症丨浅谈扩展属性与多用户设计

    说明 Abp vNext基础篇的文章还差一个单元测试模块就基本上完成了我争取10.1放假之前给大家赶稿出来,后面我们会开始进阶篇,开始拆一些东西,具体要做的事我会单独开一个文章来讲 缘起 本篇文章缘起 ...

  8. [Abp vNext 源码分析] - 11. 用户的自定义参数与配置

    一.简要说明 文章信息: 基于的 ABP vNext 版本:1.0.0 创作日期:2019 年 10 月 23 日晚 更新日期:暂无 ABP vNext 针对用户可编辑的配置,提供了单独的 Volo. ...

  9. [Abp vNext 源码分析] - 12. 后台作业与后台工作者

    一.简要说明 文章信息: 基于的 ABP vNext 版本:1.0.0 创作日期:2019 年 10 月 24 日晚 更新日期:暂无 ABP vNext 提供了后台工作者和后台作业的支持,基本实现与原 ...

  10. consul系列文章02---替换掉.netcore的配置文件

    如果是开发微服务的项目,多个服务的配置管理起来比较麻烦,需要集中管理,也就是需要有配置中心: consul集成配置中心的思路:读取配置文件时不在从本地的应用中读取,而是从consul的KEY/valu ...

随机推荐

  1. Mapper that could not be found

    现象1 mapper 资源扫不到 resources 建的是 目录 ,不是 package 所以如果直接 a.b 的方式创建,会扫描不到 mapper.xml 文件 现象2 缺少配置文件 HisDru ...

  2. PPT 配色方法

    https://www.bilibili.com/video/BV1ha411g7f5/?p=10 https://dribbble.com/ 问题1:配色太多,主题色混乱 一个主色,两辅色 问题2: ...

  3. Python分析大数据,推荐四款加速器

    在数据科学计算.机器学习.以及深度学习领域,Python 是最受欢迎的语言.Python 在数据科学领域,有非常丰富的包可以选择,numpy.scipy.pandas.scikit-learn.mat ...

  4. 流媒体传输协议之 RTMP

    作者:逸殊 审核:泰一 简介 RTMP 在可靠流式传输(TCP)的基础上提供了双向的消息多路复用服务,在通讯双方之间传输与时间相关的并行流数据,如音频,视频和数据消息.协议实现方通常为不同的消息类型指 ...

  5. Maven 命令安装指定 jar 包到本地仓库

    Maven 命令安装指定jar包到本地仓库: mvn install:install-file -Dfile=D:\Java\maven-repository\com\huawei\convert-p ...

  6. java中除法结果不对。

    今天遇一个非常简单地计算,计算结果居然是不对0,查了一些前辈们的资料动手实验了一下,实验结果和代码分享给大家.需要计算的公式:(7/10)*0.8 结果居然不是0.56 而是 0,最后找到原因(7/1 ...

  7. 解决ssh远程登录Too many authentication failures报错

    远程登录失败,报错,造成无法登录的情况,原因为:多次输入密码失败导致登录异常. 解决方案: 1.登录主机:vi /etc/ssh/sshd_config 2.找到MaxAuthTries,修改数值变大 ...

  8. 用C#实现最小二乘法(用OxyPlot绘图)✨

    最小二乘法介绍 最小二乘法(Least Squares Method)是一种常见的数学优化技术,广泛应用于数据拟合.回归分析和参数估计等领域.其目标是通过最小化残差平方和来找到一组参数,使得模型预测值 ...

  9. kubernetes scc 故障排查小记

    1. 故障现象 环境在跑自动化测试时打印 error: [ ERROR ] Opening output file '/output.xml' failed: Read-only file syste ...

  10. centos7使用nginx+uwsgi部署python django项目

    在django框架中,我们一般直接通过python manage.py runserver来启动提供服务,但是如果生产环境此方法不可行,而且容易导致异常退出,于是需要借助uwsgi来作为守护进程. 操 ...