天天看點

Prism架構中的Bootstrapper和依賴注入

  首先在介紹本節内容之前,首先來看看官方文檔來如何描述Prism 應用的初始化吧!A Prism application requires registration and configuration during the application startup process—this is known as bootstrapping the application,也就是說在在一個Prism應用程式開始之前首先要做的就是注冊和配注應用程式,我們以此為突破點,一步步來剖析。

     1 What is a Bootstrapper?

     Bootstrapper 是一個通過Prism Library來初始化一個應用程式的,我們來看看一個Bootstrapping process的基本步驟:

Prism架構中的Bootstrapper和依賴注入

  2  什麼是依賴注入?

  使用 Prism Library建立的應用程式依賴于容器提供的依賴注入, Prism Library提供了兩種依賴注入容器:即Unity和MEF(Managed Extensibility Framework),在Prism應用程式中,Bootstrapper的一個重要的職責是建立shell(MainWindow),這是因為Shell依賴于各種服務,比如Region Manager服務,而這些服務恰恰是在Shell在建立之前需要被建立的。

     如果你确認是建立MEF或者是Unity容器作為依賴注入容器,那麼正确的方法是建立一個類繼承自這些容器,然後重寫這些基類的虛方法和抽象方法,其中最重要的幾個方法是:CreateShell()、InitializeShell()、CreateModuleCatalog(),現在來分别來介紹下這些方法。

  CreateShell():Creates the shell or main window of the application.具體的寫法如下面的代碼所示:

protected override DependencyObject CreateShell()
        {
             return this.Container.Resolve<Shell>();
            //return ServiceLocator.Current.GetInstance<Shell>();         
        }
      

     仔細看,裡面寫了兩個方法,都是可以的,這裡的Shell是一個Window,是主程式,Container是定義在基類UnityBootstrapper中的一個方法,我們通過代碼發現通過ServiceLocator.Current.GetInstance<Shell>(); 也能夠達到同樣的效果,那我們來看一看ServiceLocator到底是什麼,實際上ServiceLocator在執行的時候也是通過調用 Container的。

  InitializeShell():在你建立了一個Shell之後,你需要通過InitializeShell()方法來確定Shell的呈現。下面以WPF程式為例來解釋下面的代碼。

protected override void InitializeShell()
 {
    Application.Current.MainWindow = Shell;
    Application.Current.MainWindow.Show();
 }
      

  CreateModuleCatalog():該方法用來建立一個IModuleCatalog,IModuleCatalog的執行個體用來記錄目前哪些子產品對于應用程式是可用的,哪些子產品是需要下載下傳的,以及目前子產品的歸屬。

      下面以一個具體的類來描述如何建立IModuleCatalog的執行個體。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Prism.Modularity;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Diagnostics;
using System.IO;

namespace X.Core.Infrastructure
{
    public class AggregateModuleCatalog : IModuleCatalog
    {
        private List<IModuleCatalog> catalogs = new List<IModuleCatalog>();

        public AggregateModuleCatalog()
        {
            this.catalogs.Add(new ModuleCatalog());
        }

        public ReadOnlyCollection<IModuleCatalog> Catalogs
        {
            get
            {
                return this.catalogs.AsReadOnly();
            }
        }

        public void AddCatalog(IModuleCatalog catalog)
        {
            if (catalog == null)
            {
                throw new ArgumentNullException("catalog");
            }
            this.catalogs.Add(catalog);
        }


        public IEnumerable<ModuleInfo> Modules
        {
            get
            {
                return this.Catalogs.SelectMany(x => x.Modules);
            }
        }

        public IEnumerable<ModuleInfo> GetDependentModules(ModuleInfo moduleInfo)
        {
            var catalog = this.catalogs.Single(x => x.Modules.Contains(moduleInfo));
            return catalog.GetDependentModules(moduleInfo);
        }

        public IEnumerable<ModuleInfo> CompleteListWithDependencies(IEnumerable<ModuleInfo> modules)
        {
            var modulesGroupedByCatalog = modules.GroupBy<ModuleInfo, IModuleCatalog>(module => this.catalogs.Single(catalog => catalog.Modules.Contains(module)));
            return modulesGroupedByCatalog.SelectMany(x => x.Key.CompleteListWithDependencies(x));
        }

        public void Initialize()
        {
            foreach (var catalog in this.Catalogs)
            {
                catalog.Initialize();
            }
        }

        public void AddModule(ModuleInfo moduleInfo)
        {
            this.catalogs[0].AddModule(moduleInfo);
        }

    }
}
      

  最後一個需要進行重載的就是ConfigureModuleCatalog()這個方法,這個方法主要是用來配置IModuleCatalog的。

     這裡也貼出一段代碼來看看怎麼重載上述的方法。  

protected override void ConfigureModuleCatalog()
        {
            var catalog = ((X.Core.Infrastructure.AggregateModuleCatalog)ModuleCatalog);

            if (!System.IO.Directory.Exists(@".\Apps"))
            {
                System.IO.Directory.CreateDirectory(@".\Apps");
            }
            if (!System.IO.Directory.Exists(@".\Modules"))
            {
                System.IO.Directory.CreateDirectory(@".\Modules");
            }

            foreach (string dic in System.IO.Directory.GetDirectories(@".\Apps"))
            {
                DirectoryModuleCatalog catApp = new DirectoryModuleCatalog() { ModulePath = dic };
                catalog.AddCatalog(catApp);
            }
            foreach (string dic in System.IO.Directory.GetDirectories(@".\Modules"))
            {
                DirectoryModuleCatalog catApp = new DirectoryModuleCatalog() { ModulePath = dic };
                catalog.AddCatalog(catApp);
            }

            ConfigurationModuleCatalog configCatalog = new ConfigurationModuleCatalog();
            catalog.AddCatalog(configCatalog);
        }
      

  需要特别注意的是這裡我們使用的是DirectoryModuleCatalog,來加載某一個路徑下面的DLL,這些都是需要特别說明的,我們首先來看看對于DirectoryModuleCatalog的官方解釋:Represets a catalog created from a directory on disk.代表的是一個從磁盤上建立的一個目錄,DirectoryModuleCatalog将會掃描目前目錄下的内容,定位那些繼承自IModule的類,并且将他們添加到目錄中去,通過反射的方式,目前程式集會加載到一個新的應用程式域中,當目前程式集被銷毀的時候,目前應用程式域将會被摧毀。

     下面再介紹另外一段簡單的加載應用程式域的方法,此方法不再是加載某一個路徑下的應用程式。     

protected override void ConfigureModuleCatalog()
        {
            base.ConfigureModuleCatalog();
            ModuleCatalog moduleCatalog = (ModuleCatalog)this.ModuleCatalog;
            moduleCatalog.AddModule(typeof(HelloWorldModule.MyHelloWorldModule));        

        }
      

  這裡直接加載MyHelloWorldModule,這個程式子產品繼承自IModule,下面貼出目前類的代碼。

using Microsoft.Practices.Prism.Modularity;
using Microsoft.Practices.Prism.Regions;

namespace HelloWorldModule
{
    public class MyHelloWorldModule : IModule
    {
        private readonly IRegionViewRegistry regionViewRegistry;

        public MyHelloWorldModule(IRegionViewRegistry registry)
        {
            this.regionViewRegistry = registry;   
        }

        public void Initialize()
        {
            regionViewRegistry.RegisterViewWithRegion("MainRegion", typeof(Views.HelloWorldView));
        }
    }
}
      

  這裡有一個特别重要的内容,就是通過RegisterViewWithRegion這個函數将目前子產品注入到MainRegion中去,那麼這個MainRegion到底是哪個區域呢?

     我們再來看看Shell的前台中有些什麼?     

<Window x:Class="HelloWorld.Shell"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:cal="http://www.codeplex.com/prism"
    Title="Hello World" Height="300" Width="300">
    <ItemsControl Name="MainRegion" cal:RegionManager.RegionName="MainRegion" />
</Window>
      

  看到了嗎?當Prism加載MyHelloWorldModule這個子產品的時候,會比對RegionManager.RegionName中的 MainRegion,并且将目前子產品加載到ItemsControl 中去,關于這一點,在後續的系類中,我都會重點去介紹,下一節我将會重點介紹Prism中的一些重點服務以及建立和配置UnityBootstrapper中的容器......