美文网首页ABPABP
ABP 源码解析 三. 模块化

ABP 源码解析 三. 模块化

作者: 诸葛_小亮 | 来源:发表于2018-08-16 22:42 被阅读53次

    介绍

    此系列文章主要是对ABP源码进行解读,初探作者在创造ABP的框架思路,和使用到的设计模式进行。
    通过解读ABP源码,可以提升ABP使用方式,可以提升编码意识,提高面向对象编程思想。

    《ABP 源码解析 二. IOC初始化》中介绍了ABP是如何实现IOC容器的。

    本篇文章主要介绍ABP中的根基之一:ABP模块体系。


    UML

    UML类图
    • DependsOnAttribute: 定义模块依赖的特性
    • AbpModuleInfo: abp模块信息
    • AbpModule: abp模块基类,所有自定义模块必须继承该类
    • AbpModuleCollection: abp模块集合
    • IAbpModuleManager: 定义abp模块管理器接口,提供初始化模块、开始模块、关闭模块等功能
    • AbpModuleManager: abp模块管理器实现

    AbpBootstrapper中,调用一下代码开始初始化模块

    
                    // 初始化模块管理器
                    _moduleManager = IocManager.Resolve<AbpModuleManager>();
                    
                    // 初始化模块
                    _moduleManager.Initialize(StartupModule);
                    
                    // 启动模块
                    _moduleManager.StartModules();
    

    abp模块初始化主要有一下步骤:

    1. 初始化模块: 加载模块信息
    2. 启动模块: 对模块根据依赖关系进行排序,并且调用模块的三个方法:PreInitialize Initialize PostInitialize

    源代码分析

    1. 初始化模块信息

    初始化模块信息,主要做两个事情: 根据启动模块类型初始化模块集合和加载所有模块数据

      /// <summary>
            /// 初始化模块
            /// </summary>
            /// <param name="startupModule"></param>
            public virtual void Initialize(Type startupModule)
            {
                _modules = new AbpModuleCollection(startupModule);
                LoadAllModules();
            }
    

    2. 加载模块

    加载模块过程中,主要做如下内容

    1. 查找所有模块
    2. 注册模块
    3. 创建模块
    4. 确保模块顺序,主要是确保启动模块为最后启动和Kernel模块为第一个启动
    5. 设置模块依赖关系
    
            /// <summary>
            /// 加载 所有模块
            /// </summary>
            private void LoadAllModules()
            {
                Logger.Debug("Loading Abp modules...");
    
                List<Type> plugInModuleTypes;
                // 查找素有模块
                var moduleTypes = FindAllModuleTypes(out plugInModuleTypes).Distinct().ToList();
    
                Logger.Debug("Found " + moduleTypes.Count + " ABP modules in total.");
    
                // 注册模块
                RegisterModules(moduleTypes);
    
                // 创建模块
                CreateModules(moduleTypes, plugInModuleTypes);
    
                // 确保模块顺序
                _modules.EnsureKernelModuleToBeFirst();
                _modules.EnsureStartupModuleToBeLast();
    
                // 设置依赖关系
                SetDependencies();
    
                Logger.DebugFormat("{0} modules loaded.", _modules.Count);
            }
    

    3. 设置依赖关系

    主要是根据DependsOnAttribute 特性设置模块依赖,动态计算 moduleInfo的依赖的模块顺序

    /// <summary>
            /// 设置依赖关系
            /// </summary>
            private void SetDependencies()
            {
                foreach (var moduleInfo in _modules)
                {
                    moduleInfo.Dependencies.Clear();
    
                    //Set dependencies for defined DependsOnAttribute attribute(s).
                    // 根据 DependsOnAttribute 特性设置依赖
                    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);
                        }
                    }
                }
            }
    

    4. 运行模块

    运行模块有两个事情要做:获取排好序的模块和按顺序执行模块初始化代码PreInitialize Initialize PostInitialize

    
            /// <summary>
            /// 运行模块
            /// </summary>
            public virtual void StartModules()
            {
                var sortedModules = _modules.GetSortedModuleListByDependency();
                sortedModules.ForEach(module => module.Instance.PreInitialize());
                sortedModules.ForEach(module => module.Instance.Initialize());
                sortedModules.ForEach(module => module.Instance.PostInitialize());
            }
    

    5. 模块排序

    模块排序主要是根据AbpModuleInfo中的Dependencies进行排序

    
            /// <summary>
            /// Sorts modules according to dependencies.
            /// If module A depends on module B, A comes after B in the returned List.
            /// 获取排序的模块类别
            /// </summary>
            /// <returns>Sorted list</returns>
            public List<AbpModuleInfo> GetSortedModuleListByDependency()
            {
                var sortedModules = this.SortByDependencies(x => x.Dependencies);
                EnsureKernelModuleToBeFirst(sortedModules);
                EnsureStartupModuleToBeLast(sortedModules, StartupModuleType);
                return sortedModules;
            }
    
    /// <summary>
            /// Sort a list by a topological sorting, which consider their  dependencies
            /// </summary>
            /// <typeparam name="T">The type of the members of values.</typeparam>
            /// <param name="source">A list of objects to sort</param>
            /// <param name="getDependencies">Function to resolve the dependencies</param>
            /// <returns></returns>
            public static List<T> SortByDependencies<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> getDependencies)
            {
                /* See: http://www.codeproject.com/Articles/869059/Topological-sorting-in-Csharp
                 *      http://en.wikipedia.org/wiki/Topological_sorting
                 */
    
                var sorted = new List<T>();
                var visited = new Dictionary<T, bool>();
    
                foreach (var item in source)
                {
                    SortByDependenciesVisit(item, getDependencies, sorted, visited);
                }
    
                return sorted;
            }
    

    6. 运行模块初始化代码

    每个模块在程序启动时需要运行三个方法

    1. PreInitialize:应用程序启动的第一个的事件事件,在这里以写依赖注入之前运行的代码
    2. Initialize:模块内注册依赖
    3. PostInitialize:应用程序启动的最后调用的事件
    
            /// <summary>
            /// This is the first event called on application startup. 
            /// Codes can be placed here to run before dependency injection registrations.
            /// 应用程序启动的第一个的事件事件
            /// 在这里以写依赖注入之前运行的代码
            /// </summary>
            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()
            {
                
            }
    

    7. 模块卸载

    当abp应用程序关闭时,调用该方法,以便释放资源

    
            /// <summary>
            /// 关闭模块
            /// </summary>
            public virtual void ShutdownModules()
            {
                Logger.Debug("Shutting down has been started");
    
                var sortedModules = _modules.GetSortedModuleListByDependency();
                sortedModules.Reverse();
                sortedModules.ForEach(sm => sm.Instance.Shutdown());
    
                Logger.Debug("Shutting down completed.");
            }
    
    

    测试代码

    模块测试代码主要测试了如下内容:

    1. 模块加载
    2. 模块排序
    3. 插件模块
    模块测试
     public class AbpAssemblyFinder_Tests: TestBaseWithLocalIocManager
        {
            [Fact(DisplayName = "获取模块以及信息集")]
            public void Should_Get_Module_And_Additional_Assemblies()
            {
                //Arrange
                var bootstrapper = AbpBootstrapper.Create<MyStartupModule>(options =>
                {
                    options.IocManager = LocalIocManager;
                });
    
                bootstrapper.Initialize();
    
                //Act
                var assemblies = bootstrapper.IocManager.Resolve<AbpAssemblyFinder>().GetAllAssemblies();
    
                //Assert
                assemblies.Count.ShouldBe(3);
    
                assemblies.Any(a => a == typeof(MyStartupModule).GetAssembly()).ShouldBeTrue();
                assemblies.Any(a => a == typeof(AbpKernelModule).GetAssembly()).ShouldBeTrue();
                assemblies.Any(a => a == typeof(FactAttribute).GetAssembly()).ShouldBeTrue();
            }
    
            public class MyStartupModule : AbpModule
            {
                public override Assembly[] GetAdditionalAssemblies()
                {
                    return new[] {typeof(FactAttribute).GetAssembly()};
                }
            }
        }
    
    public class PlugInModuleLoading_Tests : TestBaseWithLocalIocManager
        {
            [Fact()]
            public void Should_Load_All_Modules()
            {
                //Arrange
                var bootstrapper = AbpBootstrapper.Create<MyStartupModule>(options =>
                {
                    options.IocManager = LocalIocManager;
                });
    
                bootstrapper.PlugInSources.AddTypeList(typeof(MyPlugInModule));
    
                bootstrapper.Initialize();
    
                //Act
                var modules = bootstrapper.IocManager.Resolve<IAbpModuleManager>().Modules;
    
                //Assert
                modules.Count.ShouldBe(6);
    
                modules.Any(m => m.Type == typeof(AbpKernelModule)).ShouldBeTrue();
                modules.Any(m => m.Type == typeof(MyStartupModule)).ShouldBeTrue();
                modules.Any(m => m.Type == typeof(MyModule1)).ShouldBeTrue();
                modules.Any(m => m.Type == typeof(MyModule2)).ShouldBeTrue();
                modules.Any(m => m.Type == typeof(MyPlugInModule)).ShouldBeTrue();
                modules.Any(m => m.Type == typeof(MyPlugInDependedModule)).ShouldBeTrue();
    
                modules.Any(m => m.Type == typeof(MyNotDependedModule)).ShouldBeFalse();
            }
    
            [DependsOn(typeof(MyModule1), typeof(MyModule2))]
            public class MyStartupModule: AbpModule
            {
    
            }
    
            public class MyModule1 : AbpModule
            {
                
            }
    
            public class MyModule2 : AbpModule
            {
    
            }
            
            public class MyNotDependedModule : AbpModule
            {
    
            }
    
            [DependsOn(typeof(MyPlugInDependedModule))]
            public class MyPlugInModule : AbpModule
            {
                
            }
    
            public class MyPlugInDependedModule : AbpModule
            {
                
            }
        }
    

    总结

    结合前两篇的文章

    可以初步理清ABP的启动过程

    1. 通过AbpBootstrapper启动整个abp框架
    2. 通过IocManager 初始化Ioc容器数据
    3. 通过AbpModuleManager 初始化模块数据

    abp模块启动之后,整个abp框架就可以正常运行起来了。


    我的公众号

    相关文章

      网友评论

        本文标题:ABP 源码解析 三. 模块化

        本文链接:https://www.haomeiwen.com/subject/clxvvftx.html