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提供了一个通用的接口来实现,我们可以很方 ...
随机推荐
- PHPCMS后台密码忘记解决办法
什么是PHPCMS? PHPCMS是一款网站管理软件.该软件采用模块化开发,支持多种分类方式,使用它可方便实现个性化网站的设计.开发与维护.它支持众多的程序组合,可轻松实现网站平台迁移,并可广泛满足各 ...
- ASP.net 常用服务器控件
新人初学,有错请指,大神轻喷. .net中有HTML控件和标准服务器控件. 老师教学用的是vs2010是这样分的,不知道15里是不是这样. 如果使用HTML控件我们就会发现页面中加载了原本HTML代码 ...
- Java Thread 的 run() 与 start() 的区别
Java Thread 的使用 Java Thread 的 run() 与 start() 的区别 Java Thread 的 sleep() 和 wait() 的区别 1. ...
- Java学习笔记(03)
一.回顾运算符: 一.控制语句 1.1 顺序结构 (最常见的) 特点:代码从上往下依次执行
- 【JQ+锚标记实现点击页面回到顶部】
前言:今天想写个页面常用到的[点击回到页面顶部或是首页的功能],生活和职场一样,总会有低谷的时候,这个时候咱也别怂.别怂.别怂,说三遍!那都不是事,工作没了,再找呗,就像我上周五,团队解散那天,我是笑 ...
- mfc的OnInitDialog的返回值
以前从未注意过初始化函数的返回值,今天看到书中所述,以后可能用得上. OnInitDialog的返回值告诉windows如何处置输入焦点,如果返回 TRUE,则windows将输入焦点指派给制表键控制 ...
- VMware Tools安装小结
背景介绍:在VMware上装完ArchLinux后,窗口太小,操作不方便.查询后得知VMware Tools没有自动安装,需要手动安装. 官方安装说明:在 Linux 虚拟机中手动安装或升级 VMwa ...
- 由于Replication,DBCC Shrink不能收缩Log File
使用Backup创建测试环境之后,发现testdb的Log File过大,达到400GB,由于测试环境实际上不需要这么大的Log Space,占用400GB的Disk Space实在浪费Disk Re ...
- ASP.NET MVC下的四种验证编程方式
ASP.NET MVC采用Model绑定为目标Action生成了相应的参数列表,但是在真正执行目标Action方法之前,还需要对绑定的参数实施验证以确保其有效性,我们将针对参数的验证成为Model绑定 ...
- Windows Live Writer 的昨日荣光
今天这一篇文章,想写一写Windows Live Writer这款博客编辑器(最早的一个版本是2007年发布的).毫不夸张地说,这是为数不多的几款所见即所得的编辑器之一,当然,它的运行速度慢也是一个众 ...