ABP vNext系列文章01---模块化
一、模块化应用
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."); }