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

返回ABP系列html

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

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

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

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

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

1、摘要

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

ABP 框架提供了建立和组装模块的基础,一个模块可以依赖于另外一个模块。在一般状况 下,一个程序集就能够当作是一个模块。在 ABP 框架中,一个模块经过一个类来定义,而这 个类要继承自 AbpModule。ui

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

2、基本概念

下面的例子,咱们开发一个能够在多个不一样应用中被调用MybolgApplication模块,代码以下:插件

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

ABP框架会扫描全部的程序集,而且发现AbpModule类中全部已经导入的全部类,若是你已经建立了包含多个程序集的应用,对于ABP,咱们的建议是为每个程序集建立一个Module(模块)。

生命周期:

在一个应用中,abp框架调用了Module模块的一些指定的方法来进行启动和关闭模块的操做。咱们能够重载这些方法来完成咱们本身的任务。

ABP框架经过依赖关系的顺序来调用这些方法,假如:模块A依赖于模块B,那么模块B要在模块A以前初始化,模块启动的方法顺序以下:


一、PreInitialize-B
二、PreInitialize-A
三、Initialize-B
四、Initialize-A
五、PostInitialize-B
六、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的方法了。

3、基本原理

一、获取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();
            }

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

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

三、循环相关的依赖,把他们填在到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

 四、控制反转

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);
                }
            }
        }
    }
}

 

4、ABP底层如何描述Module

AbpModule:模块抽象类

AbpModuleInfo:模块信息

AbpModuleCollection:AbpModuleInfo的集合

IAbpModuleManager:模块管理接口

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

IModuleFinder:负责找全部模块的接口

DefaultModuleFinder:实现IModuleFinder接口

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

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

5、Module注册到ABP底层流程

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

相关文章
相关标签/搜索