本篇作为我ABP介绍的第三篇文章,这次想讲下模块的,ABP文档已经有模块这方面的介绍,但是它只讲到如何使用模块,我想详细讲解下它模块的设计思路。

ABP 框架提供了创建和组装模块的基础,一个模块能够依赖于另一个模块。在通常情况 下,一个程序集就可以看成是一个模块。在 ABP 框架中,一个模块通过一个类来定义,而这 个类要继承自 AbpModule。

其实它的设计思路很简单:

1、加载bin目录下的所有dll

  1. public class WebAssemblyFinder : IAssemblyFinder
  2. {
  3. /// <summary>
  4. /// This return all assemblies in bin folder of the web application.
  5. /// </summary>
  6. /// <returns>List of assemblies</returns>
  7. public List<Assembly> GetAllAssemblies()
  8. {
  9. var assembliesInBinFolder = new List<Assembly>();
  10.  
  11. var allReferencedAssemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToList();
  12. var dllFiles = Directory.GetFiles(HttpRuntime.AppDomainAppPath + "bin\\", "*.dll", SearchOption.TopDirectoryOnly).ToList();
  13.  
  14. foreach (string dllFile in dllFiles)
  15. {
  16. var locatedAssembly = allReferencedAssemblies.FirstOrDefault(asm => AssemblyName.ReferenceMatchesDefinition(asm.GetName(), AssemblyName.GetAssemblyName(dllFile)));
  17. if (locatedAssembly != null)
  18. {
  19. assembliesInBinFolder.Add(locatedAssembly);
  20. }
  21. }
  22.  
  23. return assembliesInBinFolder;
  24. }
  25. }

2、循环判断获取所有与AbpModule的Types有关

  1. public static bool IsAbpModule(Type type)
  2. {
  3. return
  4. type.IsClass &&
  5. !type.IsAbstract &&
  6. typeof(AbpModule).IsAssignableFrom(type);
  7. }

并递归获取没有只在所有的DependsOnAttribute,把他们填在到modules集合中(请详细看AbpModule.FindDependedModuleTypes方法)

  1. private static ICollection<Type> AddMissingDependedModules(ICollection<Type> allModules)
  2. {
  3. var initialModules = allModules.ToList();
  4. foreach (var module in initialModules)
  5. {
  6. FillDependedModules(module, allModules);
  7. }
  8.  
  9. return allModules;
  10. }
  11.  
  12. private static void FillDependedModules(Type module, ICollection<Type> allModules)
  13. {
  14. foreach (var dependedModule in AbpModule.FindDependedModuleTypes(module))
  15. {
  16. if (!allModules.Contains(dependedModule))
  17. {
  18. allModules.Add(dependedModule);
  19. FillDependedModules(dependedModule, allModules);
  20. }
  21. }
  22. }
  1. public static List<Type> FindDependedModuleTypes(Type moduleType)
  2. {
  3. if (!IsAbpModule(moduleType))
  4. {
  5. throw new AbpInitializationException("This type is not an ABP module: " + moduleType.AssemblyQualifiedName);
  6. }
  7.  
  8. var list = new List<Type>();
  9.  
  10. if (moduleType.IsDefined(typeof(DependsOnAttribute), true))
  11. {
  12. var dependsOnAttributes = moduleType.GetCustomAttributes(typeof(DependsOnAttribute), true).Cast<DependsOnAttribute>();
  13. foreach (var dependsOnAttribute in dependsOnAttributes)
  14. {
  15. foreach (var dependedModuleType in dependsOnAttribute.DependedModuleTypes)
  16. {
  17. list.Add(dependedModuleType);
  18. }
  19. }
  20. }
  21.  
  22. return list;
  23. }

所有关于模块的重要代码都在AbpModuleManager中,在上面我们已经加载了所有的模块的类型,那么ABP到底有多少个Modules呢

在我下载的Demo中包含了十三个Module,都继承字AbpModule类

3、既然我得到了所有的moduleTypes了,那么我就通过Castle Windsor循环注册了,并反转

  1. private void LoadAll()
  2. {
  3. Logger.Debug("Loading Abp modules...");
  4. //通过bin加载所有的module集合
  5. var moduleTypes = AddMissingDependedModules(_moduleFinder.FindAll());
  6. Logger.Debug("Found " + moduleTypes.Count + " ABP modules in total.");
  7.  
  8. //通过castle windsor注册所有的模块 Register to IOC container.
  9. foreach (var moduleType in moduleTypes)
  10. {
  11. if (!AbpModule.IsAbpModule(moduleType))
  12. {
  13. throw new AbpInitializationException("This type is not an ABP module: " + moduleType.AssemblyQualifiedName);
  14. }
  15.  
  16. if (!_iocManager.IsRegistered(moduleType))
  17. {
  18. _iocManager.Register(moduleType);
  19. }
  20. }
  21.  
  22. //模块反转并添加到_modules中 Add to module collection
  23. foreach (var moduleType in moduleTypes)
  24. {
  25. var moduleObject = (AbpModule)_iocManager.Resolve(moduleType);
  26.  
  27. moduleObject.IocManager = _iocManager;
  28. moduleObject.Configuration = _iocManager.Resolve<IAbpStartupConfiguration>();
  29.  
  30. _modules.Add(new AbpModuleInfo(moduleObject));
  31.  
  32. Logger.DebugFormat("Loaded module: " + moduleType.AssemblyQualifiedName);
  33. }
  34.  
  35. //确保AbpKernelModule是_modules中第一个module,AbpKernelModule must be the first module
  36. var startupModuleIndex = _modules.FindIndex(m => m.Type == typeof(AbpKernelModule));
  37. if (startupModuleIndex > 0)
  38. {
  39. var startupModule = _modules[startupModuleIndex];
  40. _modules.RemoveAt(startupModuleIndex);
  41. _modules.Insert(0, startupModule);
  42. }
  43.  
  44. SetDependencies();
  45.  
  46. Logger.DebugFormat("{0} modules loaded.", _modules.Count);
  47. }

上面代码有注释,详细请看注释

4、就是初始化所有模块的事件,早AbpModule中作者定义了三个事件,在实践应用在我们会依次执行下面三个方法

  1. public virtual void PreInitialize()
  2. {
  3.  
  4. }
  5.  
  6. /// <summary>
  7. /// This method is used to register dependencies for this module.
  8. /// </summary>
  9. public virtual void Initialize()
  10. {
  11.  
  12. }
  13.  
  14. /// <summary>
  15. /// This method is called lastly on application startup.
  16. /// </summary>
  17. public virtual void PostInitialize()
  18. {
  19.  
  20. }

在一个应用中,ABP 框架调用了 Module 模块的一些指定的方法来进行启动和关闭模块的 操作。我们可以重载这些方法来完成我们自己的任务。 ABP 框架通过依赖关系的顺序来调用这些方法,

假如:模块 A 依赖于模块 B,那么模块 B 要在模块 A 之前初始化,模块启动的方法顺序如下:

1) PreInitialize-B

2) PreInitialize-A

3) Initialize-B

4) Initialize-A

5) PostInitialize-B

6) PostInitialize-A

那么我们是怎么执行上面的方法的呢,方案在AbpModuleManager的InitializeModules方法中

  1. public virtual void InitializeModules()
  2. {
  3. LoadAll();
  4.  
  5. var sortedModules = _modules.GetSortedModuleListByDependency();
  6.  
  7. sortedModules.ForEach(module => module.Instance.PreInitialize());
  8. sortedModules.ForEach(module => module.Instance.Initialize());
  9. sortedModules.ForEach(module => module.Instance.PostInitialize());
  10. }

那么我们在自定义模块的时候是要重写上面三个方法的,有点像管道的事件,我们会依次执行这些事件,现在我们随便看个AbpEntityFrameworkModule模块它重写了其中两个

  1. public override void PreInitialize()
  2. {
  3. IocManager.AddConventionalRegistrar(new EntityFrameworkConventionalRegisterer());
  4. }
  5.  
  6. public override void Initialize()
  7. {
  8. IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
  9.  
  10. IocManager.IocContainer.Register(
  11. Component.For(typeof (IDbContextProvider<>))
  12. .ImplementedBy(typeof (UnitOfWorkDbContextProvider<>))
  13. .LifestyleTransient()
  14. );
  15.  
  16. RegisterGenericRepositories();
  17. }

一般的我们都要在Initialize方法中加上这句IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly()); 也就是注册当前程序集,IOC初始化用的哦,好了,至此终于把ABP模块的思路讲完啦,大家可以配合ABP的文档的相关章节进行研究,希望对初学者有帮助。

参考文章:

https://github.com/ABPFrameWorkGroup/AbpDocument2Chinese

ABP之模块分析的更多相关文章

  1. ABP源码分析四十五:ABP ZERO中的EntityFramework模块

    AbpZeroDbContext:配置ABP.Zero中定义的entity的Dbset EntityFrameworkModelBuilderExtensions:给PrimitiveProperty ...

  2. 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入

    使用react全家桶制作博客后台管理系统   前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...

  3. ABP源码分析四十六:ABP ZERO中的Ldap模块

    通过AD作为用户认证的数据源.整个管理用户认证逻辑就在LdapAuthenticationSource类中实现. LdapSettingProvider:定义LDAP的setting和提供Defaut ...

  4. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

  5. ABP源码分析三:ABP Module

    Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ...

  6. ABP源码分析四:Configuration

    核心模块的配置 Configuration是ABP中设计比较巧妙的地方.其通过AbpStartupConfiguration,Castle的依赖注入,Dictionary对象和扩展方法很巧妙的实现了配 ...

  7. ABP源码分析五:ABP初始化全过程

    ABP在初始化阶段做了哪些操作,前面的四篇文章大致描述了一下. 为个更清楚的描述其脉络,做了张流程图以辅助说明.其中每一步都涉及很多细节,难以在一张图中全部表现出来.每一步的细节(会涉及到较多接口,类 ...

  8. ABP源码分析七:Setting 以及 Mail

    本文主要说明Setting的实现以及Mail这个功能模块如何使用Setting. 首先区分一下ABP中的Setting和Configuration. Setting一般用于需要通过外部配置文件(或数据 ...

  9. ABP源码分析八:Logger集成

    ABP使用Castle日志记录工具,并且可以使用不同的日志类库,比如:Log4Net, NLog, Serilog... 等等.对于所有的日志类库,Castle提供了一个通用的接口来实现,我们可以很方 ...

随机推荐

  1. Servlet程序中玩验证码

    验证码思想:所谓验证码就是产生若干随机数,存放到session中,然后在servlet中获取session中的该值与页面输入值相比较,进而判断正误.   产生验证码的方法: 随机数放在图片中,封装为一 ...

  2. 《Entity Framework 6 Recipes》中文翻译系列 (41) ------ 第七章 使用对象服务之标识关系中使用依赖实体与异步查询保存

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 7-7  标识关系中使用依赖实体 问题 你想在标识关系中插入,更新和删除一个依赖实体 ...

  3. 剖析twemproxy前言

    又是喜闻乐见的新坑,前面的mysql协议,当我在解读go-mysql包的时候,会重新讲到,至于Leetcode的更新会与go语言同步.关于这个redis的新坑,目前打算通过剖析twemproxy源码来 ...

  4. KnockoutJS 3.X API 第七章 其他技术(7) 微任务

    注意:本文档适用于Knockout 3.4.0及更高版本. Knockout的微任务队列 Knockout的微任务队列支持调度任务尽可能快地运行,同时仍然是异步的,努力安排它们在发生I / O,回流或 ...

  5. VMware 安装虚拟机安装MAC (OSX10_11)

    一.简述前言: 1.本案例是基于 VMware Workstation Pro(专业版)12 上创建一个MAC操作系统(版本 :OSX10_11),下面的步骤基本上和安装其他类型的虚拟机没有什么区别. ...

  6. g++编译流程

    测试程序test.cpp如下所示: #include <iostream> using namespace std; #define MAX 9 int main() { //just f ...

  7. Util应用程序框架公共操作类(二):数据类型转换公共操作类(源码篇)

    上一篇介绍了数据类型转换的一些情况,可以看出,如果不进行封装,有可能导致比较混乱的代码.本文通过TDD方式把数据类型转换公共操作类开发出来,并提供源码下载. 我们在 应用程序框架实战十一:创建VS解决 ...

  8. 解决Oracle SQL Developer无法连接远程服务器的问题

    在使用Oracle SQL Developer连接远程服务器的时候,出现如下的错误 在服务器本地是可以正常连接的.这个让人想起来,跟SQL Server的一些设计有些类似,服务器估计默认只在本地监听, ...

  9. 【记录】EF Code First 实体关联,如何添加、修改实体?

    在使用 EF Code First 的时候,我们经常会对项目中的 Entry 进行一对多.多对多的映射配置,这时候就会产生主实体和子实体的概念,我们在添加.修改他们的时候,有时候会产生一些问题,比如添 ...

  10. T-Sql(二)事务(Transaction)

    今天讲下T-Sql语法中事务的用法,事务在项目中一般用的很少,主要用于转账,或是一些多表操作,第一步完成不了滚回,不执行接下的步骤.要么都不完成要么都完成,这是事务的特征. 语法很简单,示例代码如下: ...