IoC模式最权威的总结和解释,应该是Martin Fowler的那篇文章“Inversion of Control Containers and the Dependency Injection pattern”,其中提到了三种依赖注入的方式,即构造方法注入(constructor injection)、setter方法注入(setter injection)以及接口注入(interface injection)。下面让我们详细看一下这三种方式的特点及其相互之间的差别。
当你来到酒吧,想要喝杯啤酒的时候,通常会直接招呼服务生,“伙计,来杯啤酒!”,让他为你送来一杯清凉解渴的啤酒。同样地,作为被注入对象,要想让IoC Service Provider为其提供服务,并将所需要的被依赖对象送过来,也需要通过某种方式通知对方。
(1)构造方法注入
顾名思义,构造方法注入,就是被注入对象可以通过在其构造方法中声明依赖对象的参数列表,让外部(通常是IoC容器)知道它需要哪些依赖对象。例如:
1
2
3
4
5
|
public
A(B b,C c)
{
this
.b = b;
this
.c = c;
}
|
IoC Service Provider会检查被注入对象的构造方法,取得它所需要的依赖对象列表,进而为其注入相应的对象。同一个对象是不可能被构造两次的,因此,被注入对象的构造乃至其整个生命周期,应该是由IoC Service Provider来管理的。
构造方法注入方式比较直观,对象被构造完成后,即进入就绪状态,可以马上使用。
这就好比你刚进酒吧的门,服务生已经将你喜欢的啤酒摆上了桌面一样。坐下就可马上享受一份清凉与惬意。
(2)setter 方法注入
对于JavaBean对象来说,通常会通过setXXX()和getXXX()方法来访问对应属性。这些setXXX()方法统称为setter方 法,getXXX()当然就称为getter方法。通过setter方法,可以更改相应的对象属性,通过getter方法,可以获得相应属性的状态。所 以,当前对象只要为其依赖对象所对应的属性添加setter方法,就可以通过setter方法将相应的依赖对象设置到被注入对象中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public
class
A
{
private
B b;
private
C c;
public
B getb() {
return
b;
}
public
void
setb(B b) {
this
.b = b;
}
public
C getc() {
return
c;
}
public
void
setc(C c) {
this
.c = c;
}
}
|
这样,外界就可以通过调用setb和setc方法为A对象注入所依赖的对象了。
setter方法注入虽不像构造方法注入那样,让对象构造完成后即可使用,但相对来说更宽松一些,可以在对象构造完成后再注入。
这就好比你可以到酒吧坐下后再决定要点什么啤酒,可以要百威,也可以要大雪,随意性比较强。如果你不急着喝,这种方式当然是最适合你的。
(3)接口注入
相对于前两种注入方式来说,接口注入没有那么简单明了。
被注入对象如果想要IoC Service Provider为其注入依赖对象,就必须实现某个接口。这个接口提供一个方法,用来为其注入依赖对象。IoC Service Provider最终通过这些接口来了解应该为被注入对象注入什么依赖对象。
在这种情况下,实现的接口和接口中声明的方法名称都不重要。重要的是接口中声明方法的参数类型,必须是“被注入对象”所依赖对象的类型。
接口注入方式最早并且使用最多的是在一个叫做Avalon的项目中,相对于前两种依赖注入方式,接口注入比较死板和烦琐。
如果需要注入依赖对象,被注入对象就必须声明和实现另外的接口。
这就好像你同样在酒吧点啤酒,为了让服务生理解你的意思,你就必须戴上一顶啤酒杯式的帽子,看起来有点多此一举。
通常情况下,这有些让人不好接受。不过,好在这种方式也可以达到目的。
三种注入方式的比较
(1)接口注入。从注入方式的使用上来说,接口注入是现在不甚提倡的一种方式,基本处于“退役状态”。因为它强制被注入对象实现不必要的接口,带有侵入性。而构造方法注入和setter方法注入则不需要如此。
(2)构造方法注入。这种注入方式的优点就是,对象在构造完成之后,即已进入就绪状态,可以马上使用。缺点就是,当依赖对象比较多的时候,构造方法的参数 列表会比较长。而通过反射构造对象的时候,对相同类型的参数的处理会比较困难,维护和使用上也比较麻烦。而且在Java中,构造方法无法被继承,无法设置 默认值。对于非必须的依赖处理,可能需要引入多个构造方法,而参数数量的变动可能造成维护上的不便。
(3)setter方法注入。因为方法可以命名,所以setter方法注入在描述性上要比构造方法注入好一些。另外,setter方法可以被继承,允许设置默认值,而且有良好的IDE支持。缺点当然就是对象无法在构造完成后马上进入就绪状态。
综上所述,构造方法注入和setter方法注入因为其侵入性较弱,且易于理解和使用,所以是现在使用最多的注入方式;而接口注入因为侵入性较强,近年来已经不流行了。