基于DDD的.NET开发框架 - ABP模块设计
Posted Joye.Net
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于DDD的.NET开发框架 - 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 > 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 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 > 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模块设计的主要内容,如果未能解决你的问题,请参考以下文章
ABP 基于DDD的.NET开发框架 学习创建模块:任务管理