• 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方法
  • 相关阅读:
    CH the luckiest number 欧拉函数 同余
    bzoj1257余数求和 数论分块 暴力
    luogup1463 反素数
    CH3101 阶乘分解
    T10396 曹老板斗地主(中度模拟)
    NOIP 2015子串(DP)
    CF1205B Shortest Cycle(Floyd判最小环)
    P2055 [ZJOI2009]假期的宿舍(二分图匹配)
    灾后重建(最短路)
    CF1098A Sum in the tree(贪心)
  • 原文地址:https://www.cnblogs.com/zzqvq/p/10237454.html
Copyright © 2020-2023  润新知