• Prism框架中的Module概述(中)


    背景

      在上篇中我们主要介绍了Prism8中的IModuleInfoIModuleCatalog接口,通过这两个接口使我们对Prism框架中的Module有一个比较大概的了解,今天这篇文章我们主要来看一下Prism8中的最重要的一个接口IModuleManager这个接口,这个接口对应的实现ModuleManager实现了对整个加载到应用程序中的所有Module进行一个统一的管理,我们来重点看下这个ModuleManager依赖于哪些注入对象以及自身通过实现接口实现了哪些功能?

    源码分析

    1 IModuleManager接口

      我们先来看看这个接口中定义了哪些内容?

    /// <summary>
        /// Defines the interface for the service that will retrieve and initialize the application's modules.
        /// </summary>
        public interface IModuleManager
        {
            /// <summary>
            /// Gets all the <see cref="IModuleInfo"/> classes that are in the <see cref="IModuleCatalog"/>.
            /// </summary>
            IEnumerable<IModuleInfo> Modules { get; }
    
            /// <summary>
            /// Initializes the modules marked as <see cref="InitializationMode.WhenAvailable"/> on the <see cref="IModuleCatalog"/>.
            /// </summary>
            void Run();
    
            /// <summary>
            /// Loads and initializes the module on the <see cref="IModuleCatalog"/> with the name <paramref name="moduleName"/>.
            /// </summary>
            /// <param name="moduleName">Name of the module requested for initialization.</param>
            void LoadModule(string moduleName);
    
            /// <summary>
            /// Raised repeatedly to provide progress as modules are downloaded.
            /// </summary>
            event EventHandler<ModuleDownloadProgressChangedEventArgs> ModuleDownloadProgressChanged;
    
            /// <summary>
            /// Raised when a module is loaded or fails to load.
            /// </summary>
            event EventHandler<LoadModuleCompletedEventArgs> LoadModuleCompleted;
        }
    

      我们可以看到这个里面第一个定义了一个IEnumerable类型的Modules属性,这个主要用来描述当前ModuleManager用来管理的所有Modules,第二个Run方法是整个ModuleMananger中实现的核心内容,这个我们后面会重点关注,第三个LoadModule方法用于实现手动Load一个特定名称的Module,与此相对应的就是LoadModuleCompleted事件这个主要是当前Module进行Load完成后会触发此事件,另外一个ModuleDownloadProgressChanged事件主要是用来描述当前Module如果是从远程下载的那么通过此事件能够报告当前Module进行下载的Progress,上面我们仅仅是对整个接口定义的功能有一个大概上认知,后面我们将会针对这个里面的具体细节进行一一描述。
      我们先来看看这个ModuleManager的具体实现类。

    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
    using Prism.Properties;
    
    namespace Prism.Modularity
    {
        /// <summary>
        /// Component responsible for coordinating the modules' type loading and module initialization process.
        /// </summary>
        public partial class ModuleManager : IModuleManager, IDisposable
        {
            private readonly IModuleInitializer moduleInitializer;
            private IEnumerable<IModuleTypeLoader> typeLoaders;
            private HashSet<IModuleTypeLoader> subscribedToModuleTypeLoaders = new HashSet<IModuleTypeLoader>();
    
            /// <summary>
            /// Initializes an instance of the <see cref="ModuleManager"/> class.
            /// </summary>
            /// <param name="moduleInitializer">Service used for initialization of modules.</param>
            /// <param name="moduleCatalog">Catalog that enumerates the modules to be loaded and initialized.</param>
            public ModuleManager(IModuleInitializer moduleInitializer, IModuleCatalog moduleCatalog)
            {
                this.moduleInitializer = moduleInitializer ?? throw new ArgumentNullException(nameof(moduleInitializer));
                ModuleCatalog = moduleCatalog ?? throw new ArgumentNullException(nameof(moduleCatalog));
            }
    
            /// <summary>
            /// The module catalog specified in the constructor.
            /// </summary>
            protected IModuleCatalog ModuleCatalog { get; }
    
            /// <summary>
            /// Gets all the <see cref="IModuleInfo"/> classes that are in the <see cref="IModuleCatalog"/>.
            /// </summary>
            public IEnumerable<IModuleInfo> Modules => ModuleCatalog.Modules;
    
            /// <summary>
            /// Raised repeatedly to provide progress as modules are loaded in the background.
            /// </summary>
            public event EventHandler<ModuleDownloadProgressChangedEventArgs> ModuleDownloadProgressChanged;
    
            private void RaiseModuleDownloadProgressChanged(ModuleDownloadProgressChangedEventArgs e)
            {
                ModuleDownloadProgressChanged?.Invoke(this, e);
            }
    
            /// <summary>
            /// Raised when a module is loaded or fails to load.
            /// </summary>
            public event EventHandler<LoadModuleCompletedEventArgs> LoadModuleCompleted;
    
            private void RaiseLoadModuleCompleted(IModuleInfo moduleInfo, Exception error)
            {
                this.RaiseLoadModuleCompleted(new LoadModuleCompletedEventArgs(moduleInfo, error));
            }
    
            private void RaiseLoadModuleCompleted(LoadModuleCompletedEventArgs e)
            {
                this.LoadModuleCompleted?.Invoke(this, e);
            }
    
            /// <summary>
            /// Initializes the modules marked as <see cref="InitializationMode.WhenAvailable"/> on the <see cref="ModuleCatalog"/>.
            /// </summary>
            public void Run()
            {
                this.ModuleCatalog.Initialize();
    
                this.LoadModulesWhenAvailable();
            }
    
    
            /// <summary>
            /// Loads and initializes the module on the <see cref="IModuleCatalog"/> with the name <paramref name="moduleName"/>.
            /// </summary>
            /// <param name="moduleName">Name of the module requested for initialization.</param>
            public void LoadModule(string moduleName)
            {
                var module = this.ModuleCatalog.Modules.Where(m => m.ModuleName == moduleName);
                if (module == null || module.Count() != 1)
                {
                    throw new ModuleNotFoundException(moduleName, string.Format(CultureInfo.CurrentCulture, Resources.ModuleNotFound, moduleName));
                }
    
                var modulesToLoad = this.ModuleCatalog.CompleteListWithDependencies(module);
    
                this.LoadModuleTypes(modulesToLoad);
            }
    
            /// <summary>
            /// Checks if the module needs to be retrieved before it's initialized.
            /// </summary>
            /// <param name="moduleInfo">Module that is being checked if needs retrieval.</param>
            /// <returns></returns>
            protected virtual bool ModuleNeedsRetrieval(IModuleInfo moduleInfo)
            {
                if (moduleInfo == null)
                    throw new ArgumentNullException(nameof(moduleInfo));
    
                if (moduleInfo.State == ModuleState.NotStarted)
                {
                    // If we can instantiate the type, that means the module's assembly is already loaded into
                    // the AppDomain and we don't need to retrieve it.
                    bool isAvailable = Type.GetType(moduleInfo.ModuleType) != null;
                    if (isAvailable)
                    {
                        moduleInfo.State = ModuleState.ReadyForInitialization;
                    }
    
                    return !isAvailable;
                }
    
                return false;
            }
    
            private void LoadModulesWhenAvailable()
            {
                var whenAvailableModules = this.ModuleCatalog.Modules.Where(m => m.InitializationMode == InitializationMode.WhenAvailable);
                var modulesToLoadTypes = this.ModuleCatalog.CompleteListWithDependencies(whenAvailableModules);
                if (modulesToLoadTypes != null)
                {
                    this.LoadModuleTypes(modulesToLoadTypes);
                }
            }
    
            private void LoadModuleTypes(IEnumerable<IModuleInfo> moduleInfos)
            {
                if (moduleInfos == null)
                {
                    return;
                }
    
                foreach (var moduleInfo in moduleInfos)
                {
                    if (moduleInfo.State == ModuleState.NotStarted)
                    {
                        if (this.ModuleNeedsRetrieval(moduleInfo))
                        {
                            this.BeginRetrievingModule(moduleInfo);
                        }
                        else
                        {
                            moduleInfo.State = ModuleState.ReadyForInitialization;
                        }
                    }
                }
    
                this.LoadModulesThatAreReadyForLoad();
            }
    
            /// <summary>
            /// Loads the modules that are not initialized and have their dependencies loaded.
            /// </summary>
            protected virtual void LoadModulesThatAreReadyForLoad()
            {
                bool keepLoading = true;
                while (keepLoading)
                {
                    keepLoading = false;
                    var availableModules = this.ModuleCatalog.Modules.Where(m => m.State == ModuleState.ReadyForInitialization);
    
                    foreach (var moduleInfo in availableModules)
                    {
                        if ((moduleInfo.State != ModuleState.Initialized) && (this.AreDependenciesLoaded(moduleInfo)))
                        {
                            moduleInfo.State = ModuleState.Initializing;
                            this.InitializeModule(moduleInfo);
                            keepLoading = true;
                            break;
                        }
                    }
                }
            }
    
            private void BeginRetrievingModule(IModuleInfo moduleInfo)
            {
                var moduleInfoToLoadType = moduleInfo;
                IModuleTypeLoader moduleTypeLoader = this.GetTypeLoaderForModule(moduleInfoToLoadType);
                moduleInfoToLoadType.State = ModuleState.LoadingTypes;
    
                // Delegate += works differently between SL and WPF.
                // We only want to subscribe to each instance once.
                if (!this.subscribedToModuleTypeLoaders.Contains(moduleTypeLoader))
                {
                    moduleTypeLoader.ModuleDownloadProgressChanged += this.IModuleTypeLoader_ModuleDownloadProgressChanged;
                    moduleTypeLoader.LoadModuleCompleted += this.IModuleTypeLoader_LoadModuleCompleted;
                    this.subscribedToModuleTypeLoaders.Add(moduleTypeLoader);
                }
    
                moduleTypeLoader.LoadModuleType(moduleInfo);
            }
    
            private void IModuleTypeLoader_ModuleDownloadProgressChanged(object sender, ModuleDownloadProgressChangedEventArgs e)
            {
                this.RaiseModuleDownloadProgressChanged(e);
            }
    
            private void IModuleTypeLoader_LoadModuleCompleted(object sender, LoadModuleCompletedEventArgs e)
            {
                if (e.Error == null)
                {
                    if ((e.ModuleInfo.State != ModuleState.Initializing) && (e.ModuleInfo.State != ModuleState.Initialized))
                    {
                        e.ModuleInfo.State = ModuleState.ReadyForInitialization;
                    }
    
                    // This callback may call back on the UI thread, but we are not guaranteeing it.
                    // If you were to add a custom retriever that retrieved in the background, you
                    // would need to consider dispatching to the UI thread.
                    this.LoadModulesThatAreReadyForLoad();
                }
                else
                {
                    this.RaiseLoadModuleCompleted(e);
    
                    // If the error is not handled then I log it and raise an exception.
                    if (!e.IsErrorHandled)
                    {
                        this.HandleModuleTypeLoadingError(e.ModuleInfo, e.Error);
                    }
                }
            }
    
            /// <summary>
            /// Handles any exception occurred in the module typeloading process,
            /// and throws a <see cref="ModuleTypeLoadingException"/>.
            /// This method can be overridden to provide a different behavior.
            /// </summary>
            /// <param name="moduleInfo">The module metadata where the error happened.</param>
            /// <param name="exception">The exception thrown that is the cause of the current error.</param>
            /// <exception cref="ModuleTypeLoadingException"></exception>
            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1")]
            protected virtual void HandleModuleTypeLoadingError(IModuleInfo moduleInfo, Exception exception)
            {
                if (moduleInfo == null)
                    throw new ArgumentNullException(nameof(moduleInfo));
    
    
                if (!(exception is ModuleTypeLoadingException moduleTypeLoadingException))
                {
                    moduleTypeLoadingException = new ModuleTypeLoadingException(moduleInfo.ModuleName, exception.Message, exception);
                }
    
                throw moduleTypeLoadingException;
            }
    
            private bool AreDependenciesLoaded(IModuleInfo moduleInfo)
            {
                var requiredModules = this.ModuleCatalog.GetDependentModules(moduleInfo);
                if (requiredModules == null)
                {
                    return true;
                }
    
                int notReadyRequiredModuleCount =
                    requiredModules.Count(requiredModule => requiredModule.State != ModuleState.Initialized);
    
                return notReadyRequiredModuleCount == 0;
            }
    
            private IModuleTypeLoader GetTypeLoaderForModule(IModuleInfo moduleInfo)
            {
                foreach (IModuleTypeLoader typeLoader in this.ModuleTypeLoaders)
                {
                    if (typeLoader.CanLoadModuleType(moduleInfo))
                    {
                        return typeLoader;
                    }
                }
    
                throw new ModuleTypeLoaderNotFoundException(moduleInfo.ModuleName, string.Format(CultureInfo.CurrentCulture, Resources.NoRetrieverCanRetrieveModule, moduleInfo.ModuleName), null);
            }
    
            private void InitializeModule(IModuleInfo moduleInfo)
            {
                if (moduleInfo.State == ModuleState.Initializing)
                {
                    this.moduleInitializer.Initialize(moduleInfo);
                    moduleInfo.State = ModuleState.Initialized;
                    this.RaiseLoadModuleCompleted(moduleInfo, null);
                }
            }
    
            #region Implementation of IDisposable
    
            /// <summary>
            /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
            /// </summary>
            /// <remarks>Calls <see cref="Dispose(bool)"/></remarks>.
            /// <filterpriority>2</filterpriority>
            public void Dispose()
            {
                this.Dispose(true);
                GC.SuppressFinalize(this);
            }
    
            /// <summary>
            /// Disposes the associated <see cref="IModuleTypeLoader"/>s.
            /// </summary>
            /// <param name="disposing">When <see langword="true"/>, it is being called from the Dispose method.</param>
            protected virtual void Dispose(bool disposing)
            {
                foreach (IModuleTypeLoader typeLoader in this.ModuleTypeLoaders)
                {
                    if (typeLoader is IDisposable disposableTypeLoader)
                    {
                        disposableTypeLoader.Dispose();
                    }
                }
            }
    
            #endregion
        }
    }
    
    

    看这个大的方法之前我们来看看这个ModuleManager中的构造函数,构造函数包含两个部分一个是:IModuleInitializerIModuleCatalog这个里面IModuleCatalog在上一篇中就此已进行过分析,这里不在赘述我们先来看看这个IModuleInitializer接口,这个顾名思义就是对接口类型进行初始化操作,我们先来看看这个接口。

    2 IModuleInitializer接口

      我们先来看看这个接口的定义

        /// <summary>
        /// Declares a service which initializes the modules into the application.
        /// </summary>
        public interface IModuleInitializer
        {
            /// <summary>
            /// Initializes the specified module.
            /// </summary>
            /// <param name="moduleInfo">The module to initialize</param>
            void Initialize(IModuleInfo moduleInfo);
        }
    

     通过这个接口定义我们知道这里面的Initialize方法通过传入IModuleInfo参数实现对当前传入IModuleInfo进行初始化,我们来看看这个具体的过程。

    using System;
    using System.Globalization;
    using Prism.Ioc;
    
    namespace Prism.Modularity
    {
        /// <summary>
        /// Implements the <see cref="IModuleInitializer"/> interface. Handles loading of a module based on a type.
        /// </summary>
        public class ModuleInitializer : IModuleInitializer
        {
            private readonly IContainerExtension _containerExtension;
    
            /// <summary>
            /// Initializes a new instance of <see cref="ModuleInitializer"/>.
            /// </summary>
            /// <param name="containerExtension">The container that will be used to resolve the modules by specifying its type.</param>
            public ModuleInitializer(IContainerExtension containerExtension)
            {
                this._containerExtension = containerExtension ?? throw new ArgumentNullException(nameof(containerExtension));
            }
    
            /// <summary>
            /// Initializes the specified module.
            /// </summary>
            /// <param name="moduleInfo">The module to initialize</param>
            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Catches Exception to handle any exception thrown during the initialization process with the HandleModuleInitializationError method.")]
            public void Initialize(IModuleInfo moduleInfo)
            {
                if (moduleInfo == null)
                    throw new ArgumentNullException(nameof(moduleInfo));
    
                IModule moduleInstance = null;
                try
                {
                    moduleInstance = this.CreateModule(moduleInfo);
                    if (moduleInstance != null)
                    {
                        moduleInstance.RegisterTypes(_containerExtension);
                        moduleInstance.OnInitialized(_containerExtension);
                    }
                }
                catch (Exception ex)
                {
                    this.HandleModuleInitializationError(
                        moduleInfo,
                        moduleInstance?.GetType().Assembly.FullName,
                        ex);
                }
            }
    
            /// <summary>
            /// Handles any exception occurred in the module Initialization process,
            /// This method can be overridden to provide a different behavior.
            /// </summary>
            /// <param name="moduleInfo">The module metadata where the error happened.</param>
            /// <param name="assemblyName">The assembly name.</param>
            /// <param name="exception">The exception thrown that is the cause of the current error.</param>
            /// <exception cref="ModuleInitializeException"></exception>
            public virtual void HandleModuleInitializationError(IModuleInfo moduleInfo, string assemblyName, Exception exception)
            {
                if (moduleInfo == null)
                    throw new ArgumentNullException(nameof(moduleInfo));
    
                if (exception == null)
                    throw new ArgumentNullException(nameof(exception));
    
                Exception moduleException;
    
                if (exception is ModuleInitializeException)
                {
                    moduleException = exception;
                }
                else
                {
                    if (!string.IsNullOrEmpty(assemblyName))
                    {
                        moduleException = new ModuleInitializeException(moduleInfo.ModuleName, assemblyName, exception.Message, exception);
                    }
                    else
                    {
                        moduleException = new ModuleInitializeException(moduleInfo.ModuleName, exception.Message, exception);
                    }
                }
    
                throw moduleException;
            }
    
            /// <summary>
            /// Uses the container to resolve a new <see cref="IModule"/> by specifying its <see cref="Type"/>.
            /// </summary>
            /// <param name="moduleInfo">The module to create.</param>
            /// <returns>A new instance of the module specified by <paramref name="moduleInfo"/>.</returns>
            protected virtual IModule CreateModule(IModuleInfo moduleInfo)
            {
                if (moduleInfo == null)
                    throw new ArgumentNullException(nameof(moduleInfo));
    
                return this.CreateModule(moduleInfo.ModuleType);
            }
    
            /// <summary>
            /// Uses the container to resolve a new <see cref="IModule"/> by specifying its <see cref="Type"/>.
            /// </summary>
            /// <param name="typeName">The type name to resolve. This type must implement <see cref="IModule"/>.</param>
            /// <returns>A new instance of <paramref name="typeName"/>.</returns>
            protected virtual IModule CreateModule(string typeName)
            {
                Type moduleType = Type.GetType(typeName);
                if (moduleType == null)
                {
                    throw new ModuleInitializeException(string.Format(CultureInfo.CurrentCulture, Properties.Resources.FailedToGetType, typeName));
                }
    
                return (IModule)_containerExtension.Resolve(moduleType);
            }
        }
    }
    
    

     这个里面我们发现这个初始化IModuleInfo的核心是根据当前IModuleInfo构造出一个IModule对象,并且调用IModule中定义的RegisterTypes和OnInitialized方法,通常在我们的代码中我们是这样使用IModule这个接口的。

    using ModuleA.Views;
    using Prism.Ioc;
    using Prism.Modularity;
    using Prism.Regions;
    
    namespace ModuleA
    {
        public class ModuleAModule : IModule
        {
            public void OnInitialized(IContainerProvider containerProvider)
            {
                var regionManager = containerProvider.Resolve<IRegionManager>();
                regionManager.RegisterViewWithRegion("ContentRegion", typeof(ViewA));
            }
    
            public void RegisterTypes(IContainerRegistry containerRegistry)
            {
                
            }
        }
    }
    

      到了这里我们整个IModuleInitializer接口的作用就全部分析完毕。

    3 Run方法

      这个方法是整个ModuleManager中最核心的内容,上面的代码贴出来了具体的细节,这里就这个部分做一个总结:

    1. this.ModuleCatalog.Initialize();
       这个ModuleCatalog在上一篇中已经介绍过就是通过Initialize方法将不同的类型的Module逐一进行解析并添加到ModuleCatalog集合中,这个在上一篇中有细致的说明。
    2. LoadModulesWhenAvailable()
       上面一步将整个应用程序中的Module解析并加入到ModuleCatalog中,这一步就是将这些Modules中初始化模式为InitializationMode.WhenAvailable类型的Module进行逐一Load操作,如果当前Module的初始化类型是OnDemand,那么后面就是需要通过手动调用IModuleManager.LoadModule的方法进行手动加载,到了这里重点就是这个LoadModule到底进行了什么操作?经过分析代码总结如下:
    • 通过this.ModuleCatalog.CompleteListWithDependencies方法找到所有InitializationMode.WhenAvailable类型的Module并通过当前方法找到当前Module的所有依赖Module,并形成一个确定的先后加载顺序的Module依赖链条。
    • 确定前一步获取的Module的状态是否Ready,如果以及Ready的话执行内部LoadModulesThatAreReadyForLoad()方法
    • 如果上面一步Module满足State未初始化完毕并且其依赖的Module全部初始化完毕则执行IModuleInitializer中的Initialize方法完成对当前IModuleInfo的整个调用过程。

    总结

     上面的文章以IModuleManager接口为起点进行分析,通过构造函数找到其依赖项,分析其作用,后面通过重点分析Run方法来看ModuleManager是如何初始化整个Module,下篇的文章中我们会就其它的一些细节和当前ModuleManager中Run方法调用的时机来分析当前ModuleManager的调用方是如何控制整个系统的Module运作,至此整个过程将会有一个更加清晰的认知。

  • 相关阅读:
    Qt消息机制和事件、事件过滤
    QTableview 获取鼠标坐标的item(QModelIndex)
    qt 拖放dropEvent
    Qt获取控件位置,坐标总结
    Quick Easy FTP Server FTP工具文件传输使用
    Qt QDialog将窗体变为顶层窗体(activateWindow(); 和 raise() )
    makefile 通配符了解% $@ $^ $<
    QLocalServer和QLocalSocket单进程和进程通信
    RC4 加解密
    qt 拷贝文件设置进度条
  • 原文地址:https://www.cnblogs.com/seekdream/p/15898212.html
Copyright © 2020-2023  润新知