ABPABP

ABP 源码解析 三. 模块化

2018-08-16  本文已影响53人  诸葛_小亮

介绍

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

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

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


UML

UML类图

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框架就可以正常运行起来了。


我的公众号
上一篇下一篇

猜你喜欢

热点阅读