返回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)
{
return
type.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 > )
{
var kernelModule = _modules[kernelModuleIndex];
_modules.RemoveAt(kernelModuleIndex);
_modules.Insert(, kernelModule);
}
} private void SetDependencies()
{
foreach (var moduleInfo in _modules)
{
//Set dependencies according to assembly dependency
foreach (var referencedAssemblyName in moduleInfo.Assembly.GetReferencedAssemblies())
{
var referencedAssembly = Assembly.Load(referencedAssemblyName);
var dependedModuleList = _modules.Where(m => m.Assembly == referencedAssembly).ToList();
if (dependedModuleList.Count > )
{
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分层设计

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

  2. 线上分享-- 基于DDD的.NET开发框架-ABP介绍

    前言 为了能够帮助.Net开发者开拓视野,更好的把最新的技术应用到工作中,我在3月底受邀到如鹏网.net训练营直播间为各位学弟学妹们进行ABP框架的直播分享.同时为了让更多的.NET开发者了解ABP框 ...

  3. 基于DDD的.NET开发框架 - ABP仓储实现

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

  4. 基于DDD的.NET开发框架 - ABP的Entity设计思想

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

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

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

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

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

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

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

  8. 基于DDD的.NET开发框架 - ABP启动配置

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

  9. 基于DDD的.NET开发框架ABP实例,多租户 (Sass)应用程序,采用.NET MVC, Angularjs, EntityFramework-介绍

    介绍 基于ABPZERO的多租户 (Sass)应用程序,采用ASP.NET MVC, Angularjs-介绍 ASP.NET Boilerplate作为应用程序框架. ASP.NET MVC和ASP ...

随机推荐

  1. 深入解析Windows操作系统笔记——CH1概念和术语

    1.概念和工具 本章主要介绍Windows操作系统的关键概念和术语 1.概念和工具 1.1操作系统版本 1.2基础概念和术语 1.2.1Windows API 1.2.2 服务.函数和例程 1.2.3 ...

  2. Java中Date的比较(befor与after方法的缺陷)

    java.util.Date中的before和after方法只会比较到Day,不管你的date是yyyy-MM-dd HH:mm:ss还是yyyy-MM-dd格式的.最好用getTime()来比较具体 ...

  3. MySQL 中隔离级别 RC 与 RR 的区别

    1. 数据库事务ACID特性 数据库事务的4个特性: 原子性(Atomic): 事务中的多个操作,不可分割,要么都成功,要么都失败: All or Nothing. 一致性(Consistency): ...

  4. pwd, cd, ls, touch, mkdir, rmdir, rm

    学习Shell命令最好的资料当然的是$man, 绝对是查找命令的第一大杀器,但是我们有时只是想实现某个功能,甚至连这个命令是什么都不知道,又或者不想淹没在man里大段大段的英文里,大家可以参考Linu ...

  5. Android gdb 调试

    [1].终端目录设置到: proj.android[2].make文件的编译选项加上: -g -gstabs+[3].执行编译脚本: sh ./build_native.sh NDK_DEBUG=1[ ...

  6. Redhat下如何搭建NFS

    环境: OS:Red Hat Linux As 5 服务器ip:192.168.50.199客户端ip:192.168.50.200 1.服务器上创建共享目录同时修改权限mkdir /bak1/nfs ...

  7. monkeyrunner之安卓开发环境搭建(一)

    在学习monkeyrunner之前,让我们先搭建好eclipse安卓开发环境. 对于程序开发人员而言,eclipse并不陌生,它提供了一个非常广阔的平台来开发程序.同样也可以用它来开发android程 ...

  8. OpenStack 企业私有云的若干需求(7):电信行业解决方案 NFV

    自动扩展(Auto-scaling)支持 多租户和租户隔离 (multi-tenancy and tenancy isolation) 混合云(Hybrid cloud)支持 主流硬件支持.云快速交付 ...

  9. javaScript事件(五)事件类型之鼠标事件

    一.事件 二.事件流 以上内容见:javaScript事件(一)事件流 三.事件处理程序 四.IE事件处理程序 以上内容见javaScript事件(二)事件处理程序 五.事件对象 以上内容见javaS ...

  10. 在PYTHON中使用StringIO的性能提升实测(更新list-join对比)

    刚开始学习PYTHON,感觉到这个语言真的是很好用,可以快速完成功能实现. 最近试着用它完成工作中的一个任务:在Linux服务器中完成对.xml.gz文件的解析,生成.csv文件,以供SqlServe ...