zoukankan      html  css  js  c++  java
  • Prism初研究之依赖管理

    Prism初研究之依赖管理

    注意:Prism框架本身不提供指定的依赖注入容器,你可以使用其它的依赖注入容器,比如:Castle Windsor,StructureMap,和Spring.NET。示例中有使用Unity(Unity Application Block)作为容器的,也有使用MEF(Managed Extensibility Framework)的。

    关键决定:选择DI容器

    Prism提供了Unity和MEF两种可供选择的DI容器,如果选用其它的容器需要多做一些工作。

    • Unity和MEF都提供了以下一些功能:
      • 都使用容器注册类型;
      • 都使用容器注册实例;
      • 都使用命令式地方式创建注册类型的实例;
      • 都将依赖注入到构造函数中;
      • 都将依赖注入到属性中;
      • 都支持特性标注的类型依赖注入;
      • 都解析对象图中的依赖关系。
    • Unity特有的功能:
      • 能够解析具体类型而无需注册;
      • 能解析开放泛型;
      • 它截取对对象的调用,并给目标对象添加额外的功能。
    • MEF特有的功能:
      • 能发现文件夹中的程序集;
      • 使用XAP文件来下载和装配发现;
      • 它将发现的属性和集合重写为新的类型;
      • 自动导出派生类型;
      • 和.NET框架一起部署。

    考虑使用容器

    考虑在选择前:

    • 是否使用容器来注册和解析组件:
      • 考虑性能开销是否可接受,因为使用反射创建实例开销比较大;
      • 如果有很多或者深度的依赖关系,创建实例的开销会显著增加;
      • 如果一个组件没有依赖,或者也不依赖于其它类型,不用将它放入容器中;
      • 如果一个组件有一组依赖关系,并且依赖关系不会改变,不用将它放入容器中。
    • 考虑一个组件的生命周期,来决定注册为单例还是一个实例:
      • 如果组件是一个全局的服务,作为一个单一的资源来进行管理,应该将它注册为单例。比如日志服务;
      • 如果一个组件提供共享的状态给多个客户端,应该将它注册为单例;
      • 如果一个对象每次被注入时需要一个新的实例,应该将它注册为实例。
    • 考虑通过代码还是配置文件来配置容器:
      • 如果想要集中管理所有不同的服务,就使用配置文件来配置容器;
      • 如果想要按照条件注册指定的服务,就需要通过编码来配置容器;
      • 如果有模块级别的服务,就需要通过编码来配置容器,防止注册发生在模块加载前。

        有一些DI容器不支持配置文件,比如MEF。

    核心情景

    DI容器可以达到两个不低,类型的注册和依赖解析。

    类型注册

    一般来讲,有两种类型的注册方式:

    • 在容器中注册类型或者映射,在合适的时候,返回指定类型的一个实例;
    • 在容器中注册一个已经存在的对象实例,作为单例。容器会返回一个单例对象的引用。

    使用Unity容器注册类型

    在初始化过程中,一个类型允许注册其它的类型,比如视图类和服务类。注册使依赖的请求可以通过容器来实现。因此,模块的构造函数需要拥有容器的依赖注入。

    1. public class OrderModule : IModule
    2. {
    3. private readonly IRegionManager regionManager;
    4. private readonly IUnityContainer container;
    5. public OrderModule( IUnityContainer container, IRegionManager regionManager )
    6. {
    7. this.container = container;
    8. this.regionManager = regionManager;
    9. }
    10. public void Initialize()
    11. {
    12. this.container.RegisterType<IOrdersRepository, OrdersRepository>(new ContainerControlledLifetimeManager());
    13. // Show the Orders Editor view in the shell's main region.
    14. this.regionManager.RegisterViewWithRegion( "MainRegion", () => this.container.Resolve<OrdersEditorView>() );
    15. // Show the Orders Toolbar view in the shell's toolbar region.
    16. this.regionManager.RegisterViewWithRegion( "GlobalCommandsRegion", () => this.container.Resolve<OrdersToolBar>() );
    17. }
    18. }

    使用代码注册比配置文件注册的好处是,可以保证只有模块加载之后才会发生注册。

    使用MEF容器注册类型

    MEF可以使用特性系统来完成类型注册到容器:

    1. [Export(typeof(ILoggerFacade))]
    2. public class CallbackLogger: ILoggerFacade
    3. {
    4. }

    MEF还可以通过注册一个特定的实例:

    1. protected override void ConfigureContainer()
    2. {
    3. base.ConfigureContainer();
    4. this.Container.ComposeExportedValue<CallbackLogger>(this.callbackLogger);
    5. }

    注意:如果使用MEF容器,推荐使用特性系统来完成类型注册。

    依赖解析

    当一个类型依赖需要被解析时,会发生三件事:

    1. 如果类型没有被注册,容器会抛出异常。

      注意:有一些容器(包括Unity),允许未被注册的具体类型。

    2. 如果类型被注册为单例,容器会返回单例实例,如果类型是第一次被请求,容器会创建这个单例并一直维护它。

    3. 如果类型未被注册为单例,容器会返回一个新的实例对象。

      注意:MEF默认注册为单例,并且容器会维护一个单例的引用。Unity默认返回一个新的实例对象,并且容器不会维护这些实例的引用。

    Unity实例解析

    示例:Commanding QuickStart

    • 容器解析OrdersEditorView和OrdersToolBar视图的依赖关系:
    1. // OrderModule.cs
    2. public class OrderModule : IModule
    3. {
    4. public void Initialize()
    5. {
    6. this.container.RegisterType<IOrdersRepository, OrdersRepository>(new ContainerControlledLifetimeManager());
    7. //在main region显示Orders Editor 视图
    8. this.regionManager.RegisterViewWithRegion("MainRegion", () => this.container.Resolve<OrdersEditorView>());
    9. //在工具栏Region显示工具栏
    10. this.regionManager.RegisterViewWithRegon("GlobalCommandsRegion", () => this.container.Resolve<OrdersToolBar>());
    11. }
    12. }
    • OrdersEditorViewModel的构造函数依赖注入:
    1. public OrdersEditorViewModel(IOrdersRepository ordersRepository, OrdersCommandProxy commandProxy)
    2. {
    3. this.ordersRepository = ordersRepository;
    4. this.commandProxy = commandProxy;
    5. //创建虚拟的订单数据
    6. this.PopulateOrders();
    7. this.Orders = new ListCollectionView( _orders );
    8. //跟踪目前的选择
    9. this.Orders.CurrentChanged += SelectedOrderChanged
    10. this.Orders.MoveCurrentTo(null);
    11. }
    • Unity还可以使用属性来进行依赖注入,任何拥有[Dependency]特性的属性都能够进行自动的依赖解析

    MEF实例解析

    示例:Modularity with MEF QuickStart

    • 请求具体的类型
    1. protected override DependencyObject CreateShell()
    2. {
    3. return this.Container.GetExportedValue<Shell>();
    4. }
    • 通过构造函数依赖注入:示例中的ModuleA
    1. [ImportintConstructor]
    2. public ModuleA(ILoggerFacade logger, IModuleTracker moduleTracker)
    3. {
    4. if(logger == null)
    5. {
    6. throw new ArgumentNullException("logger");
    7. }
    8. if(moduleTracker == null)
    9. {
    10. throw new ArgumentNullException("moduleTracker");
    11. }
    12. this.logger = logger;
    13. this.moduleTracker = moduleTracker;
    14. this.moduleTracker.RecordModuleConstructed(WellKnownModuleNames.ModuleA);
    15. }
    • 通过属性进行依赖注入:ModuleTracker
    1. [Export(typeof(IModuleTracker))]
    2. public class ModuleTracker : IModuleTracker
    3. {
    4. [Import]
    5. private ILoggerFacade Logger;
    6. }

    在Prism中使用依赖注入容器

    Prism框架提供Unity和MEF两种依赖注册容器的支持,但是这两种DI容器并不是指定的。因为框架通过IServiceLocator接口来访问DI容器,所以DI容器可以被替换,只需要实现IServiceLocator接口即可。通常如果要替换DI容器,同时需要提供自己指定的Bootstrapper。IServiceLocator接口定义在Microsoft.Practices.ServiceLocation类库中(通用服务类库),这是一个开源的控制反转容器(IoC
    )。
    Prism提供UnityServiceLocatorAdapter和MefServiceLocatorAdapter,它们都继承自ServiceLocatorImplBase类型,后者实现了IServiceLocator接口。
    Prism中IServiceLocator的通用实现
    Prism并不指定DI容器,而是由特定的应用来指定。

    IServiceLocator

    IserviceLocator接口:

    1. public interface IServiceLocator : IServiceProvider
    2. {
    3. object GetInstance(Type serviceType);
    4. object GetInstance(Type serviceType, string key);
    5. IEnumerable<object> GetAllInstances(Type serviceType);
    6. TService GetInstance<TService>();
    7. TService GetInstance<TService>(string key);
    8. IEnumerable<TService> GetAllInstances<TService>();
    9. }

    IServiceLocator接口在Prism中通过扩展方法进行了扩展,你可以看到IServiceLocator接口只用来进行依赖解析,并不能用来进行类型注册。扩展方法如下:

    1. public static class ServiceLocatorExtensions
    2. {
    3. public static object TryResolve(this IServiceLocator locator, Type type)
    4. {
    5. if (locator == null) throw new ArgumentNullException("locator");
    6. try
    7. {
    8. return locator.GetInstance(type);
    9. }
    10. catch (ActivationException)
    11. {
    12. return null;
    13. }
    14. }
    15. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
    16. public static T TryResolve<T>(this IServiceLocator locator) where T : class
    17. {
    18. return locator.TryResolve(typeof(T)) as T;
    19. }
    20. }

    TryResolve扩展方法(Unity不支持),返回一个注册类型的实例,否则返回null。
    ModuleInitializer使用IServiceLocator接口来解析模块的加载。例如:

    1. // ModuleInitializer.cs - Initialize()
    2. IModule moduleInstance = null;
    3. try
    4. {
    5. moduleInstance = this.CreateModule(moduleInfo);
    6. moduleInstance.Initialize();
    7. }
    8. ...
    9. // ModuleInitializer.cs - CreateModule()
    10. protected virtual IModule CreateModule(string typeName)
    11. {
    12. type moduleType = Type.GetType(typeName);
    13. if (moduleType == null)
    14. {
    15. throw new ModuleInitializeExcetpion(string.Format(CultureInfo.CurrentCulture, Properties.Resources.FailedToGetType, typeName));
    16. }
    17. return (IModule)this.serviceLocator.GetInstance(moduleType);
    18. }

    扩展阅读

    DI容器的相关资料:

    · Unity Application Block on MSDN.

    · Unity community site on CodePlex.

    · Managed Extensibility Framework Overview on MSDN.

    · MEF community site on CodePlex.

    · Inversion of Control containers and the Dependency Injection pattern on Martin Fowler’s website.

    · Design Patterns: Dependency Injection in MSDN Magazine.

    · Loosen Up: Tame Your Software Dependencies for More Flexible Apps in MSDN Magazine.

    · Castle Project

    · StructureMap

    · Spring.NET





  • 相关阅读:
    Shader_ShaderForge_NGUI_流光&波纹&消融
    “PurMVC”在Unity中的应用
    springboot整合jdbc
    ajax属性详解
    FreeMarker 日期转换失败
    freemarker404解决方案(全面)
    @RequestParam,@PathParam,@PathVariable等注解区别
    @RestController和@Controller区别
    通过code去获取他的枚举
    Servlet(三)ServletContext
  • 原文地址:https://www.cnblogs.com/qianzi067/p/5804873.html
Copyright © 2011-2022 走看看