返回ABP系列

ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称。

ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应用程序的新起点,它旨在成为一个通用的WEB应用程序框架和项目模板。

ABP的官方网站:http://www.aspnetboilerplate.com

ABP官方文档:http://www.aspnetboilerplate.com/Pages/Documents

Github上的开源项目:https://github.com/aspnetboilerplate

一、摘要

研究过orchard和nopcommerce的都应该知道模块概念,ABP的模块也和他们是一回事。实现原理也都一样:应用程序一般都是先定义模块接口,然后把模块编译的dll放到固定的目录中(ABP只能放到bin下),应用程序主程序通过加载那些实现了插件接口的dll来实现插件的使用。

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

nopcommerce插件实现可以到:ASP.NET MVC5 插件化机制简单实现了解下。

二、基本概念

下面的例子,我们开发一个可以在多个不同应用中被调用MybolgApplication模块,代码如下:

        public class MyBlogApplicationModule : AbpModule //定义
        {public override void Initialize() //初始化
            {IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());//这行代码的写法基本上是不变的。它的作用是把当前程序集的特定类或接口注册到依赖注入容器中。
            }}

ABP框架会扫描所有的程序集,并且发现AbpModule类中所有已经导入的所有类,如果你已经创建了包含多个程序集的应用,对于ABP,我们的建议是为每一个程序集创建一个Module(模块)。

生命周期:

在一个应用中,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

下面是具体方法的说明:

PreInitialize 预初始化:当应用启动后,第一次会调用这个方法。在依赖注入注册之前,你可以在这个方法中指定自己的特别代码。举个例子吧:假如你创建了一个传统的登记类,那么你要先注册这个类(使用IocManager对登记类进行注册),你可以注册事件到IOC容器。

Initialize初始化:在这个方法中一般是来进行依赖注入的注册,一般我们通过IocManager.RegisterAssemblyByConvention这个方法来实现。如果你想实现自定义的依赖注入,那么请参考依赖注入的相关文档。

PostInitialize提交初始化:最后一个方法,这个方法用来解析依赖关系。

Shutdown关闭:当应用关闭以后,这个方法被调用。

模块依赖:

Abp框架会自动解析模块之间的依赖关系,但是我们还是建议你通过重载GetDependencies方法来明确的声明依赖关系。

[DependsOn(typeof(MyBlogCoreModule))]//通过注解来定义依赖关系
public class MyBlogApplicationModule : AbpModule
{public override void Initialize(){IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());}
}

例如上面的代码,我们就声明了MyBlogApplicationModule和MyBlogCoreModule的依赖关系(通过属性attribute),MyBlogApplicationModule这个应用模块依赖于MyBlogCoreModule核心模块,并且,MyBlogCoreModule核心模块会在MyBlogApplicationModule模块之前进行初始化。

自定义的模块方法:

我们自己定义的模块中可能有方法被其他依赖于当前模块的模块调用,下面的例子,假设模块2依赖于模块1,并且想在预初始化的时候调用模块1的方法。

public class MyModule1 : AbpModule
{public override void Initialize() //初始化模块
    {IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());//这里,进行依赖注入的注册。
    }public void MyModuleMethod1(){//这里写自定义的方法。
    }
}[DependsOn(typeof(MyModule1))]
public class MyModule2 : AbpModule
{private readonly MyModule1 _myModule1;public MyModule2(MyModule1 myModule1){_myModule1 = myModule1;}public override void PreInitialize(){_myModule1.MyModuleMethod1(); //调用MyModuleMethod1的方法。
    }public override void Initialize(){IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());}
}

就这样,就把模块1注入到了模块2,因此,模块2就能调用模块1的方法了。

三、基本原理

1、获取bin下全部dll

            /// <summary>/// 获取bin下全部dll/// </summary>public List<Assembly> GetAllAssemblies(){var allReferencedAssemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToList();var dllFiles = Directory.GetFiles(HttpRuntime.AppDomainAppPath + "bin\\", "*.dll", SearchOption.TopDirectoryOnly).ToList();return dllFiles.Select(dllFile => allReferencedAssemblies.FirstOrDefault(asm => AssemblyName.ReferenceMatchesDefinition(asm.GetName(), AssemblyName.GetAssemblyName(dllFile)))).Where(locatedAssembly => locatedAssembly != null).ToList();}

2、判断是否继承了AbpModule接口

            /// <summary>/// 判断与AbpModule的Types是否有关/// </summary>public static bool IsAbpModule(Type type){returntype.IsClass &&!type.IsAbstract &&typeof(AbpModule).IsAssignableFrom(type);}

3、循环相关的依赖,把他们填在到modules集合中

#region 循环相关的依赖,把他们填在到modules集合中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)) continue;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)) return list;var dependsOnAttributes = moduleType.GetCustomAttributes(typeof(DependsOnAttribute), true).Cast<DependsOnAttribute>();list.AddRange(dependsOnAttributes.SelectMany(dependsOnAttribute => dependsOnAttribute.DependedModuleTypes));return list;}#endregion

4、控制反转

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Abp.Configuration.Startup;
using Abp.Dependency;
using Castle.Core.Logging;namespace Abp.Modules
{/// <summary>/// This class is used to manage modules./// </summary>internal class AbpModuleManager : IAbpModuleManager{public ILogger Logger { get; set; }private readonly AbpModuleCollection _modules;private readonly IIocManager _iocManager;private readonly IModuleFinder _moduleFinder;public AbpModuleManager(IIocManager iocManager, IModuleFinder moduleFinder){_modules = new AbpModuleCollection();_iocManager = iocManager;_moduleFinder = moduleFinder;Logger = NullLogger.Instance;}/// <summary>/// 初始化模块/// </summary>public virtual void InitializeModules(){LoadAll(); //加载所有var sortedModules = _modules.GetSortedModuleListByDependency();//初始化Modules的事件sortedModules.ForEach(module => module.Instance.PreInitialize());sortedModules.ForEach(module => module.Instance.Initialize());sortedModules.ForEach(module => module.Instance.PostInitialize());}/// <summary>/// 关闭模块/// </summary>public virtual void ShutdownModules(){var sortedModules = _modules.GetSortedModuleListByDependency();sortedModules.Reverse();sortedModules.ForEach(sm => sm.Instance.Shutdown());}/// <summary>/// /// </summary>private void LoadAll(){Logger.Debug("Loading Abp modules...");var moduleTypes = AddMissingDependedModules(_moduleFinder.FindAll());Logger.Debug("Found " + moduleTypes.Count + " ABP modules in total.");//注册到IOC容器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中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);}EnsureKernelModuleToBeFirst();SetDependencies();Logger.DebugFormat("{0} modules loaded.", _modules.Count);}/// <summary>/// AbpKernelModule must be the first module/// </summary>private void EnsureKernelModuleToBeFirst(){var kernelModuleIndex = _modules.FindIndex(m => m.Type == typeof (AbpKernelModule));if (kernelModuleIndex > 0){var kernelModule = _modules[kernelModuleIndex];_modules.RemoveAt(kernelModuleIndex);_modules.Insert(0, kernelModule);}}private void SetDependencies(){foreach (var moduleInfo in _modules){//Set dependencies according to assembly dependencyforeach (var referencedAssemblyName in moduleInfo.Assembly.GetReferencedAssemblies()){var referencedAssembly = Assembly.Load(referencedAssemblyName);var dependedModuleList = _modules.Where(m => m.Assembly == referencedAssembly).ToList();if (dependedModuleList.Count > 0){moduleInfo.Dependencies.AddRange(dependedModuleList);}}//Set dependencies for defined DependsOnAttribute attribute(s).foreach (var dependedModuleType in AbpModule.FindDependedModuleTypes(moduleInfo.Type)){var dependedModuleInfo = _modules.FirstOrDefault(m => m.Type == dependedModuleType);if (dependedModuleInfo == null){throw new AbpInitializationException("Could not find a depended module " + dependedModuleType.AssemblyQualifiedName + " for " + moduleInfo.Type.AssemblyQualifiedName);}if ((moduleInfo.Dependencies.FirstOrDefault(dm => dm.Type == dependedModuleType) == null)){moduleInfo.Dependencies.Add(dependedModuleInfo);}}}}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);}}}}
}

四、ABP底层如何描述Module

AbpModule:模块抽象类

AbpModuleInfo:模块信息

AbpModuleCollection:AbpModuleInfo的集合

IAbpModuleManager:模块管理接口

AbpModuleManager:模块管理类实现模块管理接口

IModuleFinder:负责找所有模块的接口

DefaultModuleFinder:实现IModuleFinder接口

DependsOnAttribute:用来定义ABP模块依赖其他模块

AbpModuleInfo用于封装AbpModule的基本信息。 AbpModuleCollection则是AbpModuleInfo的集合。

五、Module注册到ABP底层流程

Abp底层框架发现Module是从AbpBootstrapper在执行Initialize方法的时候开始的,该方法会调用IAbpModuleManager实例的InitializeModules方法,这个方法接着调用DefaultModuleFinder的FindAll方法(该方法用于过滤出AbpModule的assembly),而FindAll方法调用TypeFinder得到所有的assembly. 所以只要你定义的assembly中有一个继承至AbpModule的类,并且该assembly被引用到你的项目中,那么这个Module就可以说会被Abp底层框架集成了。

基于DDD的.NET开发框架 - ABP模块设计相关推荐

  1. 基于DDD的.NET开发框架 - ABP初探

    全称:Asp.Net Boilerplate 官网地址:https://aspnetboilerplate.com/ GitHub地址:https://github.com/aspnetboilerp ...

  2. 基于DDD的.NET开发框架 - ABP领域服务

    返回ABP系列 ABP是"ASP.NET Boilerplate Project (ASP.NET样板项目)"的简称. ASP.NET Boilerplate是一个用最佳实践和流行 ...

  3. 基于DDD的.NET开发框架 - ABP依赖注入

    返回ABP系列 ABP是"ASP.NET Boilerplate Project (ASP.NET样板项目)"的简称. ASP.NET Boilerplate是一个用最佳实践和流行 ...

  4. 基于DDD的.NET开发框架 - ABP缓存Caching实现

    返回ABP系列 ABP是"ASP.NET Boilerplate Project (ASP.NET样板项目)"的简称. ASP.NET Boilerplate是一个用最佳实践和流行 ...

  5. 基于DDD的.NET开发框架 - ABP工作单元(Unit of Work)

    返回ABP系列 ABP是"ASP.NET Boilerplate Project (ASP.NET样板项目)"的简称. ASP.NET Boilerplate是一个用最佳实践和流行 ...

  6. java毕业设计——基于Java+sqlserver的通用安全模块设计与实现(毕业论文+程序源码)——安全模块

    基于Java+sqlserver的通用安全模块设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于Java+sqlserver的通用安全模块设计与实现,文章末尾附有本毕业设计的论文和源码下载地 ...

  7. 基于XLINX的PWM控制LED模块设计

    1.原理: PWM脉冲宽度调制,即调节脉冲的占空比.当输出的脉冲频率一定时,输出的脉冲占空比越大,相当于输出的有效电平越大,这样实现由FPGA来控制模拟量. 2.代码实现 PWM控制模块 module ...

  8. 基于STM32F407结合HC-SR04、TCRT5000模块 设计的智能小车(下篇)

    开始语 书接上文 https://blog.csdn.net/canoe1996/article/details/121979751?spm=1001.2014.3001.5501,前面博主做了一个可 ...

  9. 一种基于NTC热敏电阻测量的温度模块

    一种基于NTC热敏电阻测量的温度模块 设计初衷和经历: 设计这块温度采集模块时,最初的用途在冰箱的检测线,通过485总线的方式,采集每个工位的冰箱关键节点的温度.后因为工作变动,加以改进,增加了三相电 ...

最新文章

  1. php第一行空白,网页头部多出一行空白问题的解决方法 (PHP文件头BOM问题)
  2. prometheus连续查询_Prometheus 不完全避坑指南
  3. php查询排序,php如何对查询结果集进行排序
  4. 前端http请求跨域问题解决
  5. Android系统(237)---OTA升级基本信息介绍
  6. 顺骐名车汽车服务器中心,骐风k7电动汽车怎么样?骐风k7电动汽车介绍
  7. 【网络安全工程师面试合集】—黑客常用的端口及攻击方法汇总
  8. RAC11g使用数据泵导入导出报ORA-6512,ORA-25306,ORA-39079错
  9. 拉格朗日对偶(Lagrange duality)
  10. php indexof(,JavaScript indexOf() 方法
  11. Spring boot 开发 GA/T1400 协议之注册、保活、注销、校时功能
  12. skyline 系列 3 -TerraBuilder的使用 、mpt的创建和发布
  13. Android手机中取得当前时区(以GMT形式)
  14. Pytorch为什么总要设置随机种子
  15. 淘宝补单发货地不一样可以吗?正确补单流程是什么?
  16. 【PyTorch】如何取得预训练模型的标签label列表(以 Alexnet 在 ImageNet 上的预训练模型为例)
  17. 聊聊jvm的内存结构, 以及各种结构的作用
  18. 什么是Tower,Rack, blade服务器?
  19. [石器时代StoneAge]之 我与石器时代二三事(一) 再次相遇
  20. 经济学十大原理(四)人们会对激励做出反应

热门文章

  1. AIX5安装bash
  2. C# 计算时间差 用timespan函数
  3. 一个Java对象到底占多大内存?(转)
  4. Java中的引用类型(强引用、弱引用)和垃圾回收
  5. jdk动态代理与cglib动态代理例子
  6. 心事一件件的了掉,希望一切都能恢复到正常
  7. css样式引入形式php,引入css样式表的四种方式介绍
  8. android新年祝福代码,讯飞输入法发布Android新春版 Biu一下敲出美好祝愿!
  9. Java动态代理生成的对象导出方法
  10. RocketMQ的Consumer详解之重复消息的解决方案