ABP模块系统

模块简介

整个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<IScopedIocResolver, ScopedIocResolver>(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<SettingDefinitionManager>().Initialize();
    IocManager.Resolve<FeatureManager>().Initialize();
    IocManager.Resolve<PermissionManager>().Initialize();
    IocManager.Resolve<LocalizationManager>().Initialize();
    IocManager.Resolve<NotificationDefinitionManager>().Initialize();
    IocManager.Resolve<NavigationManager>().Initialize();
    if (Configuration.BackgroundJobs.IsJobExecutionEnabled)
    {
        var workerManager = IocManager.Resolve<IBackgroundWorkerManager>();
        workerManager.Start();
        workerManager.Add(IocManager.Resolve<IBackgroundJobManager>());
    }
}

PostInitialize

  • 注册一些基础设施
  • 从容器中取出一些服务,执行初始化方法
  • 启动所有后台工作者
public override void Shutdown()
{
    if (Configuration.BackgroundJobs.IsJobExecutionEnabled)
    {
        IocManager.Resolve<IBackgroundWorkerManager>().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<AbpModuleInfo>
{
    public Type StartupModuleType { get; }

    public AbpModuleCollection(Type startupModuleType)
    {
        StartupModuleType = startupModuleType;
    }
}

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

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);
}
  • 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<Type> FindAllModuleTypes(out List<Type> plugInModuleTypes)
{
    plugInModuleTypes = new List<Type>();
    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<Type> FindDependedModuleTypesRecursivelyIncludingGivenModule(Type moduleType)
{
    var list = new List<Type>();
    AddModuleAndDependenciesRecursively(list, moduleType);
    list.AddIfNotContains(typeof(AbpKernelModule));
    return list;
}
private static void AddModuleAndDependenciesRecursively(List<Type> 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<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.GetTypeInfo().IsDefined(typeof(DependsOnAttribute), true))
    {
        var dependsOnAttributes = moduleType.GetTypeInfo().GetCustomAttributes(typeof(DependsOnAttribute), true).Cast<DependsOnAttribute>();
        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<AbpModuleInfo> 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<AbpModuleInfo> 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<AbpPlugInManager>().PlugInSources.AddRange(PlugInSources);
        IocManager.Resolve<AbpStartupConfiguration>().Initialize();
        _moduleManager = IocManager.Resolve<AbpModuleManager>();
        _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<AbpModuleInfo> 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方法
posted @ 2019-01-08 10:26  KotobukiTsumugi  阅读(620)  评论(0编辑  收藏  举报