博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Ninject之旅之七:Ninject依赖注入
阅读量:4976 次
发布时间:2019-06-12

本文共 4020 字,大约阅读时间需要 13 分钟。

摘要

可以使用不同的模式向消费者类注入依赖项,向构造器里注入依赖项是其中一种。有一些遵循的模式用来注册依赖项,同时有一些需要避免的模式,因为他们经常导致不合乎需要的结果。这篇文章讲述那些跟Ninject功能相关的模式和反模式。然而,全面的介绍可以在Mark Seemann的书《Dependency Injection in .NET》中找到。

1、构造函数注入

构造函数时推荐的最常用的向一个类注册依赖项的模式。一般来说,这种模式应该经常被用作主要的注册模式,除非我们不得不使用其他的模式。在这个模式中,需要在构造函数中引入所有类依赖项列表。

问题是如果一个类有多于一个的构造函数会怎么样。尽管Ninject选择构造函数的策略是可以订制的,他默认的行为是选择那个有更多可以被Ninject解析的参数的构造函数。

因此在下面的例子中,尽管第二个构造函数有更多的参数,如果Ninject不能解析IService2,他将调用第一个构造函数。如果IService1也不能被解析,他将调用默认构造函数。如果两个依赖项都被注册了可以被解析,Ninject将调用第二个构造函数,因为他有更多的参数。

1 public class Consumer 2 { 3   private readonly IService1 dependency1; 4   private readonly IService2 dependency2; 5   public Consumer(IService1 dependency1) 6   { 7     this.dependency1 = dependency1; 8   } 9   public Consumer(IService1 dependency1, IService2 dependency2)10   {11     this.dependency1 = dependency1;12     this.dependency2 = dependency2;13   }14 }

如果上一个类中有另一个构造函数也有两个可以被解析的参数,Ninject将抛出一个ActivationException异常,通知多个构造函数有相同的优先级。

有两种方式可以重载默认的行为,显示地选择调用哪一个构造函数。第一种方式是在绑定中指出需要的构造函数:

Bind
().ToConstructor(arg => new Consumer(arg.Inject
()));

另一种方式是在需要的构造函数中使用[Inject]特性:

1 [Inject]2 public Consumer(IService1 dependency1)3 {4   this.dependency1 = dependency1;5 }

在上面的例子中,我们再第一个构造函数中使用了[Inject]特性,显式地指定在初始化类中注入依赖项时调用的构造函数。尽管第二个构造函数有更多的参数,按照Ninject默认的策略会选择第二个构造函数。

注意在多个构造函数中同时使用这个特性时会产生ActivationException异常。

2、初始化方法和属性注入

除了构造函数注入之外,Ninject支持通过初始化方法和属性setter依赖注入。我们可以通过[Inject]特性指定任意多的需要的方法和属性来注入依赖项。

尽管依赖项在类一初始化的时候就被注入,但是不能预计依赖项注入的顺序。下面的例子演示如何指定一个属性的注入:

1 [Inject]2 public IService Service3 {4   get { return dependency; }5   set { dependency = value; }6 }

下面的例子使用注入方法注册依赖项:

1 [Inject]2 public void Setup(IService dependency)3 {4     this.dependency = dependency;5 }

注意只有公有成员和公有构造函数才可以被注入,甚至internal的成员都被忽视除非Ninject配置成可以注册非公有成员。

在构造函数注入中,构造函数是单一的点,在这个点上,类被初始化后就可以使用它的所有的依赖项。但是,如果我们使用初始化方法,依赖项通过多个点以无法预期的顺序被注入。因此,不能知道在哪个方法中,所有的依赖项都已经被注入可以使用了。为了解决这个问题,Ninject提供了IInitializable接口。这个接口有一个IInitialize方法,一旦所有的依赖项都被注入,将调用这个方法:

1 public class Consumer : IInitializable 2 { 3     private IService1 dependency1; 4     private IService2 dependency2; 5     [Inject] 6     public IService Service1 7     { 8         get { return dependency1; } 9         set { dependency1 = value; }10     }11     [Inject]12     public IService Service213     {14       get { return dependency2; }15       set { dependency2 = value; }16     }17     public void Initialize()18     {19       // Consume all dependencies here20     }21 }

尽管Ninject支持使用属性和方法注入,构造函数注入应该是优先的方式。首先,构造函数注入使类更好的重用性,因为所有的类依赖项的列表是可见的。在初始化属性或方法里,类的使用者需要研究类的所有的成员或者浏览了类说明文档后(如果有的话),才能发现他的依赖项。
当使用构造函数注入的时候,类的初始化更容易。因为所有的依赖项在同一时刻被注入,我们可以很容易地在相同的地方使用这些依赖项。正如我们在前面的例子中看到的那样,在构造函数注入的场景中,注入的字段可能是只读的。因为只读字段只能在构造函数中被初始化,我们需要将他改成可写的,才可以使用初始化方法和属性对他进行注入。这将导致潜在的修改字段可读写属性的问题。
3、服务定位器

服务定位器是Martin Fowler介绍的一种很有争议的设计模式。尽管在一些特定的场景中可能有用,他一般被认为是一种反模式,需要尽可能避免。如果我们不属性这个模式,Ninject很容易地被误用成服务定位器。下面的例子示范误用Ninject kernal成一个服务定位器而不是一个DI容器:

1 public class Consumer 2 { 3   public void Consume() 4   { 5     var kernel = new StandardKernel(); 6     var depenency1 = kernel.Get
(); 7     var depenency2 = kernel.Get
(); 8     ... 9   }10 }

前面的代码有两个重大的缺点。第一个是尽管我们使用一个DI容器,但是我们不可能一直使用DI。这个类跟Ninject kernal绑在一起,然而Ninject kernal并不真的是这个类的依赖项。这个类以及他所有预期的调用类将总是不必要的依赖于kernal对象和Ninject类库。在另一方面,真正的类依赖项(IService1和IService2)对于这个类却不可见,降低了可重用性。即使我们按照下面的方式修改这个类的设计,这个问题仍然存在:

1 public class Consumer 2 { 3   private readonly IKernel kernel; 4   public Consumer(IKernel kernel) 5   { 6     this.kernel = kernel; 7   } 8   public void Consume() 9   {10     var depenency1 = kernel.Get
();11     var depenency2 = kernel.Get
();12     ...13   }14 }

前面的类仍旧依赖于Ninject类库,然后他不必要这样,他真正的依赖项仍然对调用者不可见。可以很容易地使用构造函数注入模式重构:

1 public Consumer(IService1 dependency1, IService2 dependency2)2 {3   this.dependency1 = dependency1;4   this.dependency2 = dependency2;5 }

 

posted on
2016-11-08 21:52 阅读(
...) 评论(
...)

转载于:https://www.cnblogs.com/uncle_danny/p/6044697.html

你可能感兴趣的文章
不使用storyboard模式或更换默认ViewController
查看>>
PHP数组实际占用内存大小的分析
查看>>
文本比较算法三——SUNDAY 算法
查看>>
用bcdedit.exe重建bcd
查看>>
关于元素透明
查看>>
Leetcode 516. Longest Palindromic Subsequence
查看>>
MinDoc文档管理系统搭建
查看>>
[HNOI2011] 数学作业
查看>>
在IDEA中创建Maven项目和添加tomcat
查看>>
ADO MFC SQL2000
查看>>
Hie with the Pie
查看>>
2019.01.04 bzoj2962: 序列操作(线段树+组合数学)
查看>>
ThinkPHP5集成支付宝手机网站支付接口
查看>>
设计模式之抽象工厂
查看>>
跟踪和事件
查看>>
document 和 document.all 分别什么时候用
查看>>
电梯演说模板
查看>>
通过翻译学英语
查看>>
【ASP】注册表的读写
查看>>
win7上安装macaca的报错问题
查看>>