介绍
此系列文章主要是对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模块初始化主要有一下步骤:
- 初始化模块: 加载模块信息
- 启动模块: 对模块根据依赖关系进行排序,并且调用模块的三个方法:
PreInitialize
Initialize
PostInitialize
源代码分析
1. 初始化模块信息
初始化模块信息,主要做两个事情: 根据启动模块类型初始化模块集合和加载所有模块数据
/// <summary>
/// 初始化模块
/// </summary>
/// <param name="startupModule"></param>
public virtual void Initialize(Type startupModule)
{
_modules = new AbpModuleCollection(startupModule);
LoadAllModules();
}
2. 加载模块
加载模块过程中,主要做如下内容
- 查找所有模块
- 注册模块
- 创建模块
- 确保模块顺序,主要是确保启动模块为最后启动和Kernel模块为第一个启动
- 设置模块依赖关系
/// <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. 运行模块初始化代码
每个模块在程序启动时需要运行三个方法
- PreInitialize:应用程序启动的第一个的事件事件,在这里以写依赖注入之前运行的代码
- Initialize:模块内注册依赖
- 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.");
}
测试代码
模块测试代码主要测试了如下内容:
- 模块加载
- 模块排序
- 插件模块
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的启动过程
- 通过
AbpBootstrapper
启动整个abp框架 - 通过
IocManager
初始化Ioc容器数据 - 通过
AbpModuleManager
初始化模块数据
abp模块启动之后,整个abp框架就可以正常运行起来了。
我的公众号
网友评论