博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ABP模块系统
阅读量:5173 次
发布时间:2019-06-13

本文共 12496 字,大约阅读时间需要 41 分钟。

模块简介

整个ABP框架可以说是由模块组成的,也可以认为是每一个程序集就是一个模块,但也不是绝对的。一个程序集也可以有多个模块.

在我们从ABP官网创建项目时,可以发现每个层下面都是有个xxxxxModule.cs的文件这就是模块.

[DependsOn(typeof(MyStudyApplicationModule),typeof(MyStudyEntityFrameworkModule),typeof(AbpAspNetCoreModule),typeof(AbpAspNetCoreSignalRModule))]public class MyStudyWebCoreModule : AbpModule{}

前一篇分析启动入口时,说过在中间件注册时(UseAbp)内部会从ioc容器中获取一个AbpBootStrapper对象并执行它的Initialize方法,Initialize则会从容器中获取一个AbpModuleManager模块管理者对象,执行AbpModuleManagerInitialize方法,创建AbpModuleCollection集合,然后执行LoadAllModules加载所有模块,加载所有模块完毕后,AbpModuleManagerInitialize方法结束,开始执行AbpModuleManager.StartModules启动所有的模块.

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

可以看到上述代码中模块的生命周期方法:

  • PreInitialize 预初始化
  • Initialize 初始化
  • PostInitialize 初始化完毕
    但其实还有个
  • Shutdown 销毁
    看代码可以得知先执行的是 PreInitialize->Initialize->PostInitialize 而Shutdown则在上一篇分析过,其实是在AbpBootstrapper.Dispose方法中的ShutdownModules方法执行的,也就是程序退出的时候.
    模块的用途就是在Abp框架加载的时候,执行一些初始化的操作.
    例如.你可能会在xxxxxxModule看到如下代码.
public override void Initialize(){    IocManager.RegisterAssemblyByConvention(typeof(xxxxxxModule).GetAssembly());}

这其实就是模块的初始化方法,扫描当前模块所在程序集,往ioc容器注册里面需要依赖注入的类型.

其实Abp自身也有许多模块,例如AbpKernelModule这是Abp核心模块.

public override void PreInitialize(){    IocManager.AddConventionalRegistrar(new BasicConventionalRegistrar());    IocManager.Register
(DependencyLifeStyle.Transient); IocManager.Register(typeof(IAmbientScopeProvider<>), typeof(DataContextAmbientScopeProvider<>), DependencyLifeStyle.Transient); AddAuditingSelectors(); AddLocalizationSources(); AddSettingProviders(); AddUnitOfWorkFilters(); ConfigureCaches(); AddIgnoredTypes(); AddMethodParameterValidators(); AddDefaultNotificationDistributor();}

PreInitialize

  • 注册许多拦截器和基础组件
  • 注册过滤器
public override void Initialize(){    foreach (var replaceAction in ((AbpStartupConfiguration)Configuration).ServiceReplaceActions.Values)    {        replaceAction();    }    IocManager.IocContainer.Install(new EventBusInstaller(IocManager));    IocManager.Register(typeof(IOnlineClientManager<>), typeof(OnlineClientManager<>), DependencyLifeStyle.Singleton);    IocManager.RegisterAssemblyByConvention(typeof(AbpKernelModule).GetAssembly(),new ConventionalRegistrationConfig{InstallInstallers = false});}

Initialize

  • 执行替换服务的 Action(Abp 允许用户在预加载操作替换基础设施的服务)
  • 注册事件总线基础设施
  • 注册AbpKernelModule所在程序集需要依赖注入的服务
public override void PostInitialize(){    RegisterMissingComponents();    IocManager.Resolve
().Initialize(); IocManager.Resolve
().Initialize(); IocManager.Resolve
().Initialize(); IocManager.Resolve
().Initialize(); IocManager.Resolve
().Initialize(); IocManager.Resolve
().Initialize(); if (Configuration.BackgroundJobs.IsJobExecutionEnabled) { var workerManager = IocManager.Resolve
(); workerManager.Start(); workerManager.Add(IocManager.Resolve
()); }}

PostInitialize

  • 注册一些基础设施
  • 从容器中取出一些服务,执行初始化方法
  • 启动所有后台工作者
public override void Shutdown(){    if (Configuration.BackgroundJobs.IsJobExecutionEnabled)    {        IocManager.Resolve
().StopAndWaitToStop(); }}
  • Shutdown则很简单,停止所有后台工作.

发现模块与注册模块

搜索所有定义的模块类型

我们定义好自己的模块后,Abp又是如何知道的呢?

其实在Stratup的ConfigureServices方法中已经传入了启动模块
return services.AddAbp<MyStudyWebHostModule>();
在之前 AbpBootstrapper 的 Initialize 初始化方法当中通过调用 AbpModuleManager.Initialize(StartupModule) 方法来初始化,在其内部可以看到:

public virtual void Initialize(Type startupModule){    _modules = new AbpModuleCollection(startupModule);    LoadAllModules();}

这里通过传入启动模块类型来初始化一个**_modules(AbpModuleCollection)**集合

internal class AbpModuleCollection : List
{ public Type StartupModuleType { get; } public AbpModuleCollection(Type startupModuleType) { StartupModuleType = startupModuleType; }}

创建**_modules(AbpModuleCollection)后,执行LoadAllModules**加载所有模块

private void LoadAllModules(){    Logger.Debug("Loading Abp modules...");    List
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);}
  • FindAllModuleTypes 寻找所有ABP模块
  • RegisterModules(moduleTypes) 向ioc容器以单例的形式注册找到的ABP模块
  • CreateModules(moduleTypes, plugInModuleTypes) 通过ioc容器获取模块,并创建对应的AbpModuleInfops:为什么要在包装一层呢?其实主要是因为AbpModuleInfo里包含了public List<AbpModuleInfo> Dependencies { get; }这个属性,里面记录了该模块的所有依赖模块,为了保证模块加载顺序正确
  • _modules.EnsureKernelModuleToBeFirst 将核心模块放在第一个位置,第一个初始化
  • _modules.EnsureStartupModuleToBeLast 将启动模块放在最后一个位置,最后一个初始化
  • SetDependencies 设置刚才所有创建的AbpModuleInfo的依赖顺序 ps:CreateModules里会把创建的AbpModuleInfo添加进 一开始通过启动模块类型创建的AbpModuleCollection集合private AbpModuleCollection _modules;
    这其实很容易想明白,启动模块依赖于其他模块.如果初始化的顺序不对就是有问题,所以最依赖最核心的模块必须是第一个初始化的。
    那么如何发现的呢.
    其实是通过反射获取了标记public class DependsOnAttribute : Attribute这个特性,取到模块类型.
    我们进入FindAllModuleTypes看看
private List
FindAllModuleTypes(out List
plugInModuleTypes){ plugInModuleTypes = new List
(); var modules = AbpModule.FindDependedModuleTypesRecursivelyIncludingGivenModule(_modules.StartupModuleType); foreach (var plugInModuleType in _abpPlugInManager.PlugInSources.GetAllModules()) { if (modules.AddIfNotContains(plugInModuleType)) { plugInModuleTypes.Add(plugInModuleType); } } return modules;}

哟,可以看到刚进入方法就执行了一个静态方法FindDependedModuleTypesRecursivelyIncludingGivenModule

看到Recursively就清楚了这是一个递归方法... 入参是我们的启动模块类型

public static List
FindDependedModuleTypesRecursivelyIncludingGivenModule(Type moduleType){ var list = new List
(); AddModuleAndDependenciesRecursively(list, moduleType); list.AddIfNotContains(typeof(AbpKernelModule)); return list;}private static void AddModuleAndDependenciesRecursively(List
modules, Type module){ if (!IsAbpModule(module)) { throw new AbpInitializationException("This type is not an ABP module: " + module.AssemblyQualifiedName); } if (modules.Contains(module)) { return; } modules.Add(module); var dependedModules = FindDependedModuleTypes(module); foreach (var dependedModule in dependedModules) { AddModuleAndDependenciesRecursively(modules, dependedModule); }}

这里先实例化一个type类型集合对象,保存我们所有的模块类型.然后作为入参传给AddModuleAndDependenciesRecursively

这里的话则是进行一系列的判断,之后先把入参module加入刚才的list集合,然后通过FindDependedModuleTypes寻找其依赖的模块

public static List
FindDependedModuleTypes(Type moduleType){ if (!IsAbpModule(moduleType)) { throw new AbpInitializationException("This type is not an ABP module: " + moduleType.AssemblyQualifiedName); } var list = new List
(); if (moduleType.GetTypeInfo().IsDefined(typeof(DependsOnAttribute), true)) { var dependsOnAttributes = moduleType.GetTypeInfo().GetCustomAttributes(typeof(DependsOnAttribute), true).Cast
(); foreach (var dependsOnAttribute in dependsOnAttributes) { foreach (var dependedModuleType in dependsOnAttribute.DependedModuleTypes) { list.Add(dependedModuleType); } } } return list;}

找到所有的依赖模块后,遍历,递归....这样就把所有的依赖模块都找到了.

然后回到AbpModuleManager执行 RegisterModules(moduleTypes) 注册所有模块(单例)

模块的加载顺序排序初步确定

AbpModuleCollection 提供了两个方法,一个是 EnsureKernelModuleToBeFirst一个是 EnsureStartupModuleToBeLast

public void EnsureKernelModuleToBeFirst(){    EnsureKernelModuleToBeFirst(this);}public void EnsureStartupModuleToBeLast(){    EnsureStartupModuleToBeLast(this, StartupModuleType);}public static void EnsureKernelModuleToBeFirst(List
modules){ var kernelModuleIndex = modules.FindIndex(m => m.Type == typeof(AbpKernelModule)); if (kernelModuleIndex <= 0) { //It's already the first! return; } var kernelModule = modules[kernelModuleIndex]; modules.RemoveAt(kernelModuleIndex); modules.Insert(0, kernelModule);}public static void EnsureStartupModuleToBeLast(List
modules, Type startupModuleType){ var startupModuleIndex = modules.FindIndex(m => m.Type == startupModuleType); if (startupModuleIndex >= modules.Count - 1) { //It's already the last! return; } var startupModule = modules[startupModuleIndex]; modules.RemoveAt(startupModuleIndex); modules.Add(startupModule);}
  • EnsureKernelModuleToBeFirst 找到AbpKernelModule的索引.并通过索引获取到AbpKernelModule,先移除之前索引位置,然后从索引0处的位置插入AbpKernelModule,达到AbpKernelModule是处于集合中第一个的位置
  • EnsureStartupModuleToBeLast 找到启动模块的索引,并通过索引获取到启动模块,先移除,然后插入到集合中最后一个位置.
    所以模块在进行加载的时候,第一个加载的模块一定是核心模块,最后加载的模块肯定是启动模块。

确定每个模块的依赖

AbpModuleInfo 里面有个 public List<AbpModuleInfo> Dependencies { get; }集合属性,这里面就是存当前模块所有依赖模块的。

而怎么存 存哪些模块进去,则是在加载所有模块时候 执行 SetDependencies方法中确定的

private void SetDependencies(){    foreach (var moduleInfo in _modules)    {        moduleInfo.Dependencies.Clear();        //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);            }        }    }}
  • 经过CreateModules后AbpModuleCollection中已经存在了所有的依赖,所以这里是直接遍历,寻找当前模块 所依赖的 所有模块,存入当前模块的 public List<AbpModuleInfo> Dependencies { get; }集合中

确定模块加载顺序排序完全确定

在所有模块基本信息加载完成之后,Abp 并没有在AbpModuleManagerInitialize里面来进行这个重新排序操作,而是在 StartModules 方法里面来重新排序。

public virtual void Initialize(){    ResolveLogger();    try    {        RegisterBootstrapper();        IocManager.IocContainer.Install(new AbpCoreInstaller());        IocManager.Resolve
().PlugInSources.AddRange(PlugInSources); IocManager.Resolve
().Initialize(); _moduleManager = IocManager.Resolve
(); _moduleManager.StartModules(); } catch (Exception ex) { _logger.Fatal(ex.ToString(), ex); throw; }}
  • _moduleManager.Initialize(StartupModule);//加载所有模块,确定每个模块所有的依赖模块,确定模块首尾顺序
  • _moduleManager.StartModules();//在这里进行模块重新排序
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());}

可以看到StartModules方法中首先进行的就是排序

public List
GetSortedModuleListByDependency(){ var sortedModules = this.SortByDependencies(x => x.Dependencies); EnsureKernelModuleToBeFirst(sortedModules); EnsureStartupModuleToBeLast(sortedModules, StartupModuleType); return sortedModules;}
  • SortByDependencies其实这里执行的是一个拓扑排序算法
  • 然后还是确定AbpKernelModule为集合首,startupModule为集合尾,到这里,模块顺序就已经全部排序完成.

总结

1.Configure方法中会注册ABP中间件.

2.执行AbpApplicationBuilder的InitializeAbp扩展方法,从ioc容器中获取abpBootstrapper

3.执行abpBootstrapper的Initialize方法,从Ioc容器中获取AbpModuleManager.

4.执行AbpModuleManager的Initialize方法.

  • 根据启动模块的类型初始化一个AbpModuleCollection集合
  • 执行LoadAllModules方法
    • 执行FindAllModuleTypes方法.递归找到所有模块.
    • 执行RegisterModules方法:以单例的形式注册模块到ioc容器
    • 执行CreateModules方法:
      • 从ioc容器获取模块,设置基本属性(IocManager,Configuration)
      • 将模块对象包装成AbpModuleInfo对象,添加进AbpModuleCollection集合中
    • 执行AbpModuleCollection的EnsureKernelModuleToBeFirstEnsureStartupModuleToBeLast,确定集中模块基本首尾顺序(AbpKernelModule索引为0,启动模块索引为最后一位)
    • 执行SetDependencies方法:遍历AbpModuleCollection,找到当前ModuleInfo的所有依赖模块,添加进moduleInfo.Dependencies属性集合中

5.执行AbpModuleManager的StartModules方法

  • 将AbpModuleCollection再次排序(拓扑排序),保证模块执行顺序的正确
  • 执行所有模块的PreInitialize方法
  • 执行所有模块的Initialize方法
  • 执行所有模块的PostInitialize方法

转载于:https://www.cnblogs.com/zzqvq/p/10237454.html

你可能感兴趣的文章
[转]js和jquery获取窗体高度
查看>>
Eureka的自我保护模式
查看>>
Java通过BCrypt加密
查看>>
学习总结 java基础
查看>>
SQL(1)—增删改查
查看>>
Spring+Quartz 整合二:调度管理与定时任务分离
查看>>
ubuntu下编译ffmpeg并用eclipse调试
查看>>
oracle分页查询
查看>>
微信自定义分享
查看>>
CSS基础part1
查看>>
魔兽世界服务器Trinitycore分析二:auth server的main函数
查看>>
MFC防止进程重复建立
查看>>
3. Node.js REPL(交互式解释器)
查看>>
webview滑动事件 与内部html左右滑动事件冲突问题的解决办法
查看>>
傅里叶变换学习总结
查看>>
java面试题汇总(1)
查看>>
VC Dimension -衡量模型与样本的复杂度
查看>>
android 中 ViewPager 的平常用法 ViewPager+ Views
查看>>
POJ 2449 Remmarguts' Date (SPFA + A星算法) - from lanshui_Yang
查看>>
ZOJ 1654 二分匹配基础题
查看>>