深度理解依赖注入
[2] DI的实现方式
[3] Setter Injection
[4] 除了DI,还有Service Locator
2.DI的实现方式
和上面的图1对应的是,如果我们的系统实现了依赖注入,组件间的依赖关系就变成了图2:
图2
说白了,就是要提供一个容器,由容器来完成(1)具体ServiceProvider的创建(2)ServiceUser和ServiceProvider的运行时绑定。下面我们就依次来看一下三种典型的依赖注入方式的实现。特别要说明的是,要理解依赖注入的机制,关键是理解容器的实现方式。本文后面给出的容器参考实现,均为黄忠成老师的代码,笔者仅在其中加上了一些关键注释而已。
2.1 Constructor Injection(构造器注入)
我们可以看到,在整个依赖注入的数据结构中,涉及到的重要的类型就是ServiceUser, ServiceProvider和Assembler三者,而这里所说的构造器,指的是ServiceUser的构造器。也就是说,在构造ServiceUser实例的时候,才把真正的ServiceProvider传给他:
2{
3 //其他内容,省略
4
5 public MovieLister(MovieFinder finder)
6 {
7 this.finder = finder;
8 }
9}
接下来我们看看Assembler应该如何构建:
2 MutablePicoContainer pico = new DefaultPicoContainer();
3
4 //下面就是把ServiceProvider和ServiceUser都放入容器的过程,以后就由容器来提供ServiceUser的已完成依赖注入实例,
5 //其中用到的实例参数和类型参数一般是从配置档中读取的,这里是个简单的写法。
6 //所有的依赖注入方法都会有类似的容器初始化过程,本文在后面的小节中就不再重复这一段代码了。
7 Parameter[] finderParams = {new ConstantParameter("movies1.txt")};
8 pico.registerComponentImplementation(MovieFinder.class, ColonMovieFinder.class, finderParams);
9 pico.registerComponentImplementation(MovieLister.class);
10 //至此,容器里面装入了两个类型,其中没给出构造参数的那一个(MovieLister)将依靠其在构造器中定义的传入参数类型,在容器中
11 //进行查找,找到一个类型匹配项即可进行构造初始化。
12 return pico;
13}
需要在强调一下的是,依赖并未消失,只是延后到了容器被构建的时刻。所以正如图2中您已经看到的,容器本身(更准确的说,是一个容器运行实例的构建过程)对ServiceUser和ServiceProvoder都是存在依赖关系的。所以,在这样的体系结构里,ServiceUser、ServiceProvider和容器都是稳定的,互相之间也没有任何依赖关系;所有的依赖关系、所有的变化都被封装进了容器实例的创建过程里,符合我们对服务应用的理解。而且,在实际开发中我们一般会采用配置文件来辅助容器实例的创建,将这种变化性排斥到编译期之外。
即使还没给出后面的代码,你也一定猜得到,这个container类一定有一个GetInstance(Type t)这样的方法,这个方法会为我们返回一个已经注入完毕的MovieLister。 一个简单的应用如下:
2{
3 MutablePicoContainer pico = configureContainer();
4 MovieLister lister = (MovieLister) pico.getComponentInstance(MovieLister.class);
5 Movie[] movies = lister.moviesDirectedBy("Sergio Leone");
6 assertEquals("Once Upon a Time in the West", movies[0].getTitle());
7}
上面最关键的就是对pico.getComponentInstance的调用。Assembler会在这个时候调用MovieLister的构造器,构造器的参数就是当时通过pico.registerComponentImplementation(MovieFinder.class, ColonMovieFinder.class, finderParams)设置进去的实际的ServiceProvider--ColonMovieFinder。下面请看这个容器的参考代码: