1.依赖在哪里
老马举了一个小例子,是开发一个电影列举器(MovieList),这个电影列举器需要使用一个电影查找器(MovieFinder)提供的服务,伪码如下:
1
/**//*服务的接口*/
2
public interface MovieFinder
{
3
ArrayList findAll();
4
}
5
6
/**//*服务的消费者*/
7
class MovieLister
8

{
9
public Movie[] moviesDirectedBy(String arg)
{
10
List allMovies = finder.findAll();
11
for (Iterator it = allMovies.iterator(); it.hasNext();)
{
12
Movie movie = (Movie) it.next();
13
if (!movie.getDirector().equals(arg)) it.remove();
14
}
15
return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);
16
}
17
18
/**//*消费者内部包含一个将指向具体服务类型的实体对象*/
19
private MovieFinder finder;
20
/**//*消费者需要在某一个时刻去实例化具体的服务。这是我们要解耦的关键所在,
21
*因为这样的处理方式造成了服务消费者和服务提供者的强耦合关系(这种耦合是在编译期就确定下来的)。
22
**/
23
public MovieLister()
{
24
finder = new ColonDelimitedMovieFinder("movies1.txt");
25
}
26
}2.DI的实现方式
和上面的图1对应的是,如果我们的系统实现了依赖注入,组件间的依赖关系就变成了图2:
图2
说白了,就是要提供一个容器,由容器来完成(1)具体ServiceProvider的创建(2)ServiceUser和ServiceProvider的运行时绑定。下面我们就依次来看一下三种典型的依赖注入方式的实现。特别要说明的是,要理解依赖注入的机制,关键是理解容器的实现方式。本文后面给出的容器参考实现,均为黄忠成老师的代码,笔者仅在其中加上了一些关键注释而已。
2.1 Constructor Injection(构造器注入)
我们可以看到,在整个依赖注入的数据结构中,涉及到的重要的类型就是ServiceUser, ServiceProvider和Assembler三者,而这里所说的构造器,指的是ServiceUser的构造器。也就是说,在构造ServiceUser实例的时候,才把真正的ServiceProvider传给他:
1
class MovieLister
2

{
3
//其他内容,省略
4
5
public MovieLister(MovieFinder finder)
6
{
7
this.finder = finder;
8
}
9
}
2.2 Setter Injection(设值注入)
这种注入方式和构造注入实在很类似,唯一的区别就是前者在构造函数的调用过程中进行注入,而它是通过给属性赋值来进行注入。无怪乎PicoContainer和Spring都是同时支持这两种注入方式。Spring对通过XML进行配置有比较好的支持,也使得Spring中更常使用设值注入的方式:
1
<beans>
2
<bean id="MovieLister" class="spring.MovieLister">
3
<property name="finder">
4
<ref local="MovieFinder"/>
5
property>
6
bean>
7
<bean id="MovieFinder" class="spring.ColonMovieFinder">
8
<property name="filename">
9
<value>movies1.txtvalue>
10
property>
11
bean>
12
beans>2.4 除了DI,还有Service Locator
上面提到的依赖注入只是消除ServiceUser和ServiceProvider之间的依赖关系的一种方法,还有另一种方法:服务定位器(Service Locator)。也就是说,由ServiceLocator来专门负责提供具体的ServiceProvider。当然,这样的话ServiceUser不仅要依赖于服务的接口,还依赖于ServiceContract。仍然是最早提到过的电影列举器的例子,如果使用Service Locator来解除依赖的话,整个依赖关系应当如下图所示:
图3
用起来也很简单,在一个适当的位置(比如在一组相关服务即将被调用之前)对ServiceLocator进行初始化,用到的时候就直接用ServiceLocator返回ServiceProvider实例:
1
//服务定位器的初始化
2
ServiceLocator locator = new ServiceLocator();
3
locator.loadService("MovieFinder", new ColonMovieFinder("movies1.txt"));
4
ServiceLocator.load(locator);
5
//服务定义器的使用
6
//其实这个使用方式体现了服务定位器和依赖注入模式的最大差别:ServiceUser需要显示的调用ServiceLocator,从而获取自己需要的服务对象;
7
//而依赖注入则是隐式的由容器完成了这一切。
8
MovieFinder finder = (MovieFinder) ServiceLocator.getService("MovieFinder");
9
it知识库:深度理解依赖注入,转载需保留来源!
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。