ABP之模块分析
本篇作为我ABP介绍的第三篇文章,这次想讲下模块的,ABP文档已经有模块这方面的介绍,但是它只讲到如何使用模块,我想详细讲解下它模块的设计思路。
ABP 框架提供了创建和组装模块的基础,一个模块能够依赖于另一个模块。在通常情况 下,一个程序集就可以看成是一个模块。在 ABP 框架中,一个模块通过一个类来定义,而这 个类要继承自 AbpModule。
其实它的设计思路很简单:
1、加载bin目录下的所有dll
- public class WebAssemblyFinder : IAssemblyFinder
- {
- /// <summary>
- /// This return all assemblies in bin folder of the web application.
- /// </summary>
- /// <returns>List of assemblies</returns>
- public List<Assembly> GetAllAssemblies()
- {
- var assembliesInBinFolder = new List<Assembly>();
- var allReferencedAssemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToList();
- var dllFiles = Directory.GetFiles(HttpRuntime.AppDomainAppPath + "bin\\", "*.dll", SearchOption.TopDirectoryOnly).ToList();
- foreach (string dllFile in dllFiles)
- {
- var locatedAssembly = allReferencedAssemblies.FirstOrDefault(asm => AssemblyName.ReferenceMatchesDefinition(asm.GetName(), AssemblyName.GetAssemblyName(dllFile)));
- if (locatedAssembly != null)
- {
- assembliesInBinFolder.Add(locatedAssembly);
- }
- }
- return assembliesInBinFolder;
- }
- }
2、循环判断获取所有与AbpModule的Types有关
- public static bool IsAbpModule(Type type)
- {
- return
- type.IsClass &&
- !type.IsAbstract &&
- typeof(AbpModule).IsAssignableFrom(type);
- }
并递归获取没有只在所有的DependsOnAttribute,把他们填在到modules集合中(请详细看AbpModule.FindDependedModuleTypes方法)
- private static ICollection<Type> AddMissingDependedModules(ICollection<Type> allModules)
- {
- var initialModules = allModules.ToList();
- foreach (var module in initialModules)
- {
- FillDependedModules(module, allModules);
- }
- return allModules;
- }
- private static void FillDependedModules(Type module, ICollection<Type> allModules)
- {
- foreach (var dependedModule in AbpModule.FindDependedModuleTypes(module))
- {
- if (!allModules.Contains(dependedModule))
- {
- allModules.Add(dependedModule);
- FillDependedModules(dependedModule, allModules);
- }
- }
- }
- public static List<Type> FindDependedModuleTypes(Type moduleType)
- {
- if (!IsAbpModule(moduleType))
- {
- throw new AbpInitializationException("This type is not an ABP module: " + moduleType.AssemblyQualifiedName);
- }
- var list = new List<Type>();
- if (moduleType.IsDefined(typeof(DependsOnAttribute), true))
- {
- var dependsOnAttributes = moduleType.GetCustomAttributes(typeof(DependsOnAttribute), true).Cast<DependsOnAttribute>();
- foreach (var dependsOnAttribute in dependsOnAttributes)
- {
- foreach (var dependedModuleType in dependsOnAttribute.DependedModuleTypes)
- {
- list.Add(dependedModuleType);
- }
- }
- }
- return list;
- }
所有关于模块的重要代码都在AbpModuleManager中,在上面我们已经加载了所有的模块的类型,那么ABP到底有多少个Modules呢
在我下载的Demo中包含了十三个Module,都继承字AbpModule类
3、既然我得到了所有的moduleTypes了,那么我就通过Castle Windsor循环注册了,并反转
- private void LoadAll()
- {
- Logger.Debug("Loading Abp modules...");
- //通过bin加载所有的module集合
- var moduleTypes = AddMissingDependedModules(_moduleFinder.FindAll());
- Logger.Debug("Found " + moduleTypes.Count + " ABP modules in total.");
- //通过castle windsor注册所有的模块 Register to IOC container.
- foreach (var moduleType in moduleTypes)
- {
- if (!AbpModule.IsAbpModule(moduleType))
- {
- throw new AbpInitializationException("This type is not an ABP module: " + moduleType.AssemblyQualifiedName);
- }
- if (!_iocManager.IsRegistered(moduleType))
- {
- _iocManager.Register(moduleType);
- }
- }
- //模块反转并添加到_modules中 Add to module collection
- foreach (var moduleType in moduleTypes)
- {
- var moduleObject = (AbpModule)_iocManager.Resolve(moduleType);
- moduleObject.IocManager = _iocManager;
- moduleObject.Configuration = _iocManager.Resolve<IAbpStartupConfiguration>();
- _modules.Add(new AbpModuleInfo(moduleObject));
- Logger.DebugFormat("Loaded module: " + moduleType.AssemblyQualifiedName);
- }
- //确保AbpKernelModule是_modules中第一个module,AbpKernelModule must be the first module
- var startupModuleIndex = _modules.FindIndex(m => m.Type == typeof(AbpKernelModule));
- if (startupModuleIndex > 0)
- {
- var startupModule = _modules[startupModuleIndex];
- _modules.RemoveAt(startupModuleIndex);
- _modules.Insert(0, startupModule);
- }
- SetDependencies();
- Logger.DebugFormat("{0} modules loaded.", _modules.Count);
- }
上面代码有注释,详细请看注释
4、就是初始化所有模块的事件,早AbpModule中作者定义了三个事件,在实践应用在我们会依次执行下面三个方法
- public virtual void PreInitialize()
- {
- }
- /// <summary>
- /// This method is used to register dependencies for this module.
- /// </summary>
- public virtual void Initialize()
- {
- }
- /// <summary>
- /// This method is called lastly on application startup.
- /// </summary>
- public virtual void PostInitialize()
- {
- }
在一个应用中,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方法中
- public virtual void InitializeModules()
- {
- LoadAll();
- var sortedModules = _modules.GetSortedModuleListByDependency();
- sortedModules.ForEach(module => module.Instance.PreInitialize());
- sortedModules.ForEach(module => module.Instance.Initialize());
- sortedModules.ForEach(module => module.Instance.PostInitialize());
- }
那么我们在自定义模块的时候是要重写上面三个方法的,有点像管道的事件,我们会依次执行这些事件,现在我们随便看个AbpEntityFrameworkModule模块它重写了其中两个
- public override void PreInitialize()
- {
- IocManager.AddConventionalRegistrar(new EntityFrameworkConventionalRegisterer());
- }
- public override void Initialize()
- {
- IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
- IocManager.IocContainer.Register(
- Component.For(typeof (IDbContextProvider<>))
- .ImplementedBy(typeof (UnitOfWorkDbContextProvider<>))
- .LifestyleTransient()
- );
- RegisterGenericRepositories();
- }
一般的我们都要在Initialize方法中加上这句IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly()); 也就是注册当前程序集,IOC初始化用的哦,好了,至此终于把ABP模块的思路讲完啦,大家可以配合ABP的文档的相关章节进行研究,希望对初学者有帮助。
参考文章:
https://github.com/ABPFrameWorkGroup/AbpDocument2Chinese
ABP之模块分析的更多相关文章
- ABP源码分析四十五:ABP ZERO中的EntityFramework模块
AbpZeroDbContext:配置ABP.Zero中定义的entity的Dbset EntityFrameworkModelBuilderExtensions:给PrimitiveProperty ...
- 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入
使用react全家桶制作博客后台管理系统 前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...
- ABP源码分析四十六:ABP ZERO中的Ldap模块
通过AD作为用户认证的数据源.整个管理用户认证逻辑就在LdapAuthenticationSource类中实现. LdapSettingProvider:定义LDAP的setting和提供Defaut ...
- ABP源码分析一:整体项目结构及目录
ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...
- ABP源码分析三:ABP Module
Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ...
- ABP源码分析四:Configuration
核心模块的配置 Configuration是ABP中设计比较巧妙的地方.其通过AbpStartupConfiguration,Castle的依赖注入,Dictionary对象和扩展方法很巧妙的实现了配 ...
- ABP源码分析五:ABP初始化全过程
ABP在初始化阶段做了哪些操作,前面的四篇文章大致描述了一下. 为个更清楚的描述其脉络,做了张流程图以辅助说明.其中每一步都涉及很多细节,难以在一张图中全部表现出来.每一步的细节(会涉及到较多接口,类 ...
- ABP源码分析七:Setting 以及 Mail
本文主要说明Setting的实现以及Mail这个功能模块如何使用Setting. 首先区分一下ABP中的Setting和Configuration. Setting一般用于需要通过外部配置文件(或数据 ...
- ABP源码分析八:Logger集成
ABP使用Castle日志记录工具,并且可以使用不同的日志类库,比如:Log4Net, NLog, Serilog... 等等.对于所有的日志类库,Castle提供了一个通用的接口来实现,我们可以很方 ...
随机推荐
- Servlet程序中玩验证码
验证码思想:所谓验证码就是产生若干随机数,存放到session中,然后在servlet中获取session中的该值与页面输入值相比较,进而判断正误. 产生验证码的方法: 随机数放在图片中,封装为一 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (41) ------ 第七章 使用对象服务之标识关系中使用依赖实体与异步查询保存
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 7-7 标识关系中使用依赖实体 问题 你想在标识关系中插入,更新和删除一个依赖实体 ...
- 剖析twemproxy前言
又是喜闻乐见的新坑,前面的mysql协议,当我在解读go-mysql包的时候,会重新讲到,至于Leetcode的更新会与go语言同步.关于这个redis的新坑,目前打算通过剖析twemproxy源码来 ...
- KnockoutJS 3.X API 第七章 其他技术(7) 微任务
注意:本文档适用于Knockout 3.4.0及更高版本. Knockout的微任务队列 Knockout的微任务队列支持调度任务尽可能快地运行,同时仍然是异步的,努力安排它们在发生I / O,回流或 ...
- VMware 安装虚拟机安装MAC (OSX10_11)
一.简述前言: 1.本案例是基于 VMware Workstation Pro(专业版)12 上创建一个MAC操作系统(版本 :OSX10_11),下面的步骤基本上和安装其他类型的虚拟机没有什么区别. ...
- g++编译流程
测试程序test.cpp如下所示: #include <iostream> using namespace std; #define MAX 9 int main() { //just f ...
- Util应用程序框架公共操作类(二):数据类型转换公共操作类(源码篇)
上一篇介绍了数据类型转换的一些情况,可以看出,如果不进行封装,有可能导致比较混乱的代码.本文通过TDD方式把数据类型转换公共操作类开发出来,并提供源码下载. 我们在 应用程序框架实战十一:创建VS解决 ...
- 解决Oracle SQL Developer无法连接远程服务器的问题
在使用Oracle SQL Developer连接远程服务器的时候,出现如下的错误 在服务器本地是可以正常连接的.这个让人想起来,跟SQL Server的一些设计有些类似,服务器估计默认只在本地监听, ...
- 【记录】EF Code First 实体关联,如何添加、修改实体?
在使用 EF Code First 的时候,我们经常会对项目中的 Entry 进行一对多.多对多的映射配置,这时候就会产生主实体和子实体的概念,我们在添加.修改他们的时候,有时候会产生一些问题,比如添 ...
- T-Sql(二)事务(Transaction)
今天讲下T-Sql语法中事务的用法,事务在项目中一般用的很少,主要用于转账,或是一些多表操作,第一步完成不了滚回,不执行接下的步骤.要么都不完成要么都完成,这是事务的特征. 语法很简单,示例代码如下: ...