理解Spring中的依赖注入
本文主要参照Martin Fowler的Inversion of Control Containers and the Dependency Injection pattern(URL:http://martinfowler.com/articles/injection.html)来帮助自己理解依赖注入的。由于本人比较注重实用性,于是从实例和依赖注入能够解决什么问题来展开描述。
首先我们定义一个鸭子类。
class Duck...
public void fly(String arg) {
flyBehavior.fly();
}
鸭子本身不会飞,而要借助flyBehavior这个实例。我把飞的行为定义成接口,当我想添加新的飞行行为的时候,只要实现IFlyBehavior接口就可以了。
public interface IFlyBehavior {
void fly();
}
现在看来,所有的一切都变得解耦了,但是当我要实例化“IFlyBehavior接口的实现(implementation)”RocketFlyBehavior时,问题就出现了。
class Duck...
private IFlyBehavior flyer;
public Duck() {
flyer = new RocketFlyBehavior ("path.txt");//path.txt定义了鸭子的飞行路线
}
这时,new RocketFlyBehavior的代码被放到Duck的构造函数里面。如果我自己使用这个类的时候,是不会产生任何问题。但是如果因为不可抗力,我的朋友被我的代码给深深吸引住,想要一份我的程序的副本拿去使用时,那该怎么办?如果他们都把鸭子的飞行路线按照我指定的格式,存到txt格式的文件中,并且像我一样把文件名字改为path.txt,那一切都没有问题。如果他们想要把飞行路线的文件改成别的名字,或者纯粹想用SQL Server,XML,WebService或者其他格式的txt文件来提供行走路线呢?好在我有先见之明,把飞行行为定义成接口IFlyBehavior,他们只要重新实现IFlyBehavior接口,重新定义一个类就完全不用改动“Duck类里面”fly方法中的代码了。如果现在我的朋友写了一个新的类AnotherFlyBehavior,这时候,我就必须到Duck的构造函数中把RocketFlyBehavior改写成AnotherFlyBehavior,然后重新编译代码。
Figure 1: The dependencies using a simple creation in the lister class
从图1我们能够看出“依赖”的情况是如何的。Duck类不仅依赖于IFlyBehavior接口(interface),而且还依赖于该接口的实现(implementation)RocketFlyBehavior。我们希望Duck类能够只依赖于接口,而不是实现,但我们要如何把实例化RocketFlyBehavior的代码分离出去,以及怎么样才能给IFlyBehavior flyer绑定一个实例呢?
有没有一种方法可以让我朋友编写的IFlyBehavior接口的实现类,可以不需要跟我的程序一起编译(因为我设计的时候完全不知道他们要怎么去实现这个接口)?我想要Duck类能够跟任何的IFlyBehavior的实现一起工作,而且能让别人实现完IFlyBehavior以后,该实现能够直接在我的Duck类中运行,而不需要我手动去改代码(每次new一个IFlyBehavior的实现到Duck类中时,都必须重新改代码,这是一件很麻烦的事情)。问题是我怎么才能够产生一种连接,使得Duck类不知道IFlyBehavior的实现,又能让IFlyBehavior实现类的实例正常工作。
然后就是大名鼎鼎的"Inversion of Control"以及"Dependency Injection"出场。
IoC就是Inversion of Control,控制反转。在Java开发中,IoC意味着将你设计好的类交给系统去控制,而不是在你的类内部控制。这称为控制反转。(引用自CSDN)
什么是Dependency?根据Spring.Net1.2.0的文档"5.2.3. The Objects"中某句话“references to other objects which are needed for the object to do its work: these references are also called collaborators or dependencies.”可以看出,对其他对象的引用就叫做“依赖”。什么是Injection?请继续看下面。
Forms of Dependency Injection
(依赖注入的多种形式)
依赖注入的基本概念是有一个独立出来的一个对象或程序集,能够把类里面的某个field(如flyer)与一个合适的实现(如IFlyBehavior的实现)给组装起来,形成如下的一个新的依赖关系图:(英文原文:The basic idea of the Dependency Injection is to have a separate object, an assembler, that populates a field in the lister class with an appropriate implementation for the finder interface, resulting in a dependency diagram along the lines of Figure 2)
PS:依赖注入,依我的理解为,某个独立的程序集把一个instance“赋值”(“绑定”或“注入”)给另一个instance的“reference”(或“依赖”)的一个过程。而这个过程可以在我已经编译好甚至已经投入运作的系统中进行,修改配置文件能够把多个业务类重新整合(改变配置文件需要重启)。
Figure 2: The dependencies for a Dependency Injector
PS:对比图1和图2。图1中,Duck类不仅依赖FlyBehavior,而且还依赖于RocketFlyBehavior的实例化。图2中,由于实例化RocketFlyBehavior的过程交给系统某个Assembler处理,把图1的依赖关系改变:Duck类只依赖于FlyBehavior接口。
依赖注入有三种形式,分别为Constructor Injection(在构造函数中进行注入), Setter Injection(Property的Set方法进行注入), and Interface Injection(专门为注入做接口。。。). Martin Fowler觉得名字太难记,把这些名字重命名为type 1 IoC (interface injection), type 2 IoC (setter injection) and type 3 IoC (constructor injection).在这里就不一一解释了,有需要请到http://martinfowler.com/articles/injection.html 学习。下一节将讲述如何用Spring生成代理来访问WebService。