[Abp 源碼分析]2、模塊系統

0.簡介

整個 Abp 框架由各個模塊組成,基本上能夠看作一個程序集一個模塊,不排除一個程序集有多個模塊的可能性。能夠看看他官方的這些擴展庫:html

img

能夠看到每一個項目文件下面都會有一個 xxxModule 的文件,這裏就是存放的模塊文件,一個模塊擁有四個生命週期,分別爲 PreInitialize()(預加載)、Initialize()(初始化)、PostInitialize(初始化完成)、Shutdown()(銷燬),前三個根據咱們上一篇文章的代碼能夠看到,他是先執行預加載方法,而後執行初始化,最後執行初始化完成方法,銷燬方法則是程序退出的時候執行。app

模塊的主要做用就是在 Abp 框架加載的時候程序集執行初始化操做的,好比說 Abp 庫自身的 AbpKernelModule 模塊,裏面就是各類注入基礎設施,執行初始化操做。框架

能夠看看其中代碼:ide

public sealed class AbpKernelModule : AbpModule
{
    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();
    }

    public override void Initialize()
    {
        // 這裏是執行替換服務的 Action,Abp 容許用戶在預加載操做替換基礎設施的服務
        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
            });
    }

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

    public override void Shutdown()
    {
        // 中止全部後臺工做者
        if (Configuration.BackgroundJobs.IsJobExecutionEnabled)
        {
            IocManager.Resolve<IBackgroundWorkerManager>().StopAndWaitToStop();
        }
    }
}

1.模塊發現與註冊

1.1 發現模塊

1.1.1 搜索全部定義的模塊類型

咱們定義好模塊以後,Abp 如何發現咱們的模塊呢?this

在最外部,咱們使用 services.AddAbp<TStartModule>() 的時候,就傳入了啓動模塊類型。code

在以前 AbpBootstrapperInitialize() 初始化方法當中經過調用 AbpModuleManager.Initialize(Type startupModule) 方法來初始化,在其內部能夠看到:orm

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

這裏經過傳入啓動模塊來初始化 AboModuleCollection 類。htm

internal class AbpModuleCollection : List<AbpModuleInfo>
{
    public Type StartupModuleType { get; }

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

    // 其餘代碼
}

初始化完成以後,繼續調用 LoadAllModules() 方法,這裏就開始加載模塊了。對象

private void LoadAllModules()
{
    Logger.Debug("Loading Abp modules...");

    List<Type> plugInModuleTypes;
    // 發現全部 Abp 模塊
    var moduleTypes = FindAllModuleTypes(out plugInModuleTypes).Distinct().ToList();

    Logger.Debug("Found " + moduleTypes.Count + " ABP modules in total.");

    // 註冊 Abp 模塊
    RegisterModules(moduleTypes);
    // 建立模塊對應的 AbpModuleInfo 包裝類
    CreateModules(moduleTypes, plugInModuleTypes);

    // 將核心模塊放在第一位初始化
    _modules.EnsureKernelModuleToBeFirst();
    // 將啓動模塊放在最後一位進行初始化
    _modules.EnsureStartupModuleToBeLast();

    // 設置每一個 ModuleInfo 的依賴關係
    SetDependencies();

    Logger.DebugFormat("{0} modules loaded.", _modules.Count);
}

繼續跳轉,來到內部 FindAllModuleTypes() 方法,在這個方法裏面咱們能夠看到他調用了 AbpModule 的一個靜態方法來根據其啓動模塊,以後經過啓動模塊上面的 DependsOnAttribute 特性來遞歸找到它全部的依賴模塊。blog

private List<Type> FindAllModuleTypes(out List<Type> plugInModuleTypes)
{
    plugInModuleTypes = new List<Type>();

    var modules = AbpModule.FindDependedModuleTypesRecursivelyIncludingGivenModule(_modules.StartupModuleType);
    
    // 其餘代碼
    return modules;
}

img

找到模塊以後,在 RegisterModules() 裏面經過 IocManager 的註冊方法,將全部模塊都注入到 Ioc 容器當中,注意這裏註冊的全部的 Abp 模塊都是單例對象。

1.1.2 包裝模塊信息

LoadAllModules() 方法裏面,經過 CreateModules() 方法來包裝好 ModuleInfo 類而且將其放在以前初始化完成的 AbpModuleCollection 對象 _modules 裏面。

private void CreateModules(ICollection<Type> moduleTypes, List<Type> plugInModuleTypes)
{
    foreach (var moduleType in moduleTypes)
    {
        // 解析剛纔在 RegisterModules 裏面註冊的單例模塊對象
        var moduleObject = _iocManager.Resolve(moduleType) as AbpModule;
        if (moduleObject == null)
        {
            throw new AbpInitializationException("This type is not an ABP module: " + moduleType.AssemblyQualifiedName);
        }

        // 爲這些模塊對象初始化基礎設施
        moduleObject.IocManager = _iocManager;
        moduleObject.Configuration = _iocManager.Resolve<IAbpStartupConfiguration>();

        // 包裝成爲 ModuleInfo
        var moduleInfo = new AbpModuleInfo(moduleType, moduleObject, plugInModuleTypes.Contains(moduleType));

        _modules.Add(moduleInfo);

        if (moduleType == _modules.StartupModuleType)
        {
            StartupModule = moduleInfo;
        }

        Logger.DebugFormat("Loaded module: " + moduleType.AssemblyQualifiedName);
    }
}

在每一個 ModuleInfo 對象內部都存放有該模塊的模塊類型信息,以及他的單例對象實例。

1.1.3 肯定基本的模塊加載順序

模塊在進行加載的時候,第一個加載的模塊必定是從核心模塊,最後加載的模塊確定是啓動模塊。因此,這裏的 AbpModuleCollection 提供了兩個方法,一個是 EnsureKernelModuleToBeFirst() ,一個是 EnsureStartupModuleToBeLast() 。這兩個方法的做用第一個就是將 AbpKernelModule 放在第一位,第二個就是將啓動模塊放在集合的末尾。

public static void EnsureKernelModuleToBeFirst(List<AbpModuleInfo> modules)
{
    var kernelModuleIndex = modules.FindIndex(m => m.Type == typeof(AbpKernelModule));
    if (kernelModuleIndex <= 0)
    {
        // 若是 AbpKernelModule 位於首位則不移動位置
        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)
    {
        // 若是啓動模塊位於尾部則則不移動位置
        return;
    }

    var startupModule = modules[startupModuleIndex];
    modules.RemoveAt(startupModuleIndex);
    modules.Add(startupModule);
}

1.2 依賴解析

以前這些步驟已經將咱們程序所使用到的全部模塊已經加載完成,而且進行了一個基本的排序操做,以確保咱們的模塊加載順序沒有大問題。可是僅僅這樣是不夠的, 咱們還須要確保咱們依賴的模塊比被引用的模塊要先加載,這個時候就須要肯定每一個模塊的依賴關係,而且根據這個依賴關係再次進行排序。

1.2.1 設置每一個模塊的依賴模塊

由於咱們以前爲每一個模塊包裝了一個 ModuleInfo實例,在 ModuleInfo 內部還有一個屬性,叫作:

/// <summary>
/// All dependent modules of this module.
/// </summary>
public List<AbpModuleInfo> Dependencies { get; }

因此,在 LoadAllModules() 方法裏面還調用了一個方法,叫作 SetDependencies(),這個方法也是很簡單的,遍歷已經加載完成的 _modules 集合,在裏面再根據 AbpModule 提供的 FindDependedModuleTypes() 方法來獲取該模塊的全部依賴模塊類型。找到以後,在 AbpModuleInfo 集合裏面查找對應的依賴模塊的的 ModuleInfo 信息添加到目標模塊的 Dependencies 集合內部。

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

img

1.2.2 肯定正確的模塊加載順序

在全部基本信息加載完成以後,Abp 並無在 AbpModuleManagerInitialize() 裏面來進行這個從新排序操做,而是在 StartModules() 方法裏面來從新排序。

StartModules() 經過 AbpModuleCollection 提供的 GetSortedModuleListByDependency() 方法來根據依賴項從新進行了一次排序。

public List<AbpModuleInfo> GetSortedModuleListByDependency()
{
    var sortedModules = this.SortByDependencies(x => x.Dependencies);
    EnsureKernelModuleToBeFirst(sortedModules);
    EnsureStartupModuleToBeLast(sortedModules, StartupModuleType);
    return sortedModules;
}

這裏使用的是存放在 \Abp\src\Abp\Collections\Extensions\ListExtensions.cs 的一個擴展方法 List<T> SortByDependencies<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> getDependencies),他是針對 List<T> 集合實現的一種拓撲排序。

排序以後的結果就是按照依賴關係來存放的一個集合,以後經過 List 的 Foreach 方法循環調用其三個生命週期方法便可。

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

1.2.3 擴展:拓撲排序

/// <summary>
/// Extension methods for <see cref="IList{T}"/>.
/// </summary>
public static class ListExtensions
{
    /// <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;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T">The type of the members of values.</typeparam>
    /// <param name="item">Item to resolve</param>
    /// <param name="getDependencies">Function to resolve the dependencies</param>
    /// <param name="sorted">List with the sortet items</param>
    /// <param name="visited">Dictionary with the visited items</param>
    private static void SortByDependenciesVisit<T>(T item, Func<T, IEnumerable<T>> getDependencies, List<T> sorted, Dictionary<T, bool> visited)
    {
        bool inProcess;
        var alreadyVisited = visited.TryGetValue(item, out inProcess);

        if (alreadyVisited)
        {
            if (inProcess)
            {
                throw new ArgumentException("Cyclic dependency found! Item: " + item);
            }
        }
        else
        {
            visited[item] = true;

            var dependencies = getDependencies(item);
            if (dependencies != null)
            {
                foreach (var dependency in dependencies)
                {
                    SortByDependenciesVisit(dependency, getDependencies, sorted, visited);
                }
            }

            visited[item] = false;
            sorted.Add(item);
        }
    }
}

後面專門寫文章講解一下拓撲排序,這裏貼上代碼,後面會改成文章連接的。
貼上詳解連接:
http://www.javashuo.com/article/p-pvxcyudw-hy.html

2.結語

本篇文章主要針對模塊系統進行了一個較爲詳細地分析,後面將會講解 Abp 依賴注入相關的代碼,若是你以爲對你有用請點個贊,謝謝。

3.點此跳轉到總目錄

相關文章
相關標籤/搜索