ioc的核心是DI,目的就是提供一种更简单的机制来设置组件依赖项,并在整个生命周期中管理这些依赖项。需要某些依赖项的组件通常被称为依赖对象,或者在ioc的情况下被称为目标对象。通常ioc可以分解为两种子类型:依赖注入和依赖查找,这些子类型被进一步分解为ioc服务的具体实现。通过这个定义可以清楚的看到,当谈论DI时,通常是在谈论ioc,而当谈论ioc时,并不总是在谈论DI(依赖查找也是ioc的一种形式)。使用依赖查找时,组建必须对依赖项的引用,而使用依赖注入时,依赖项将通过ioc容器注入组件,依赖查找有两种类型:依赖拉取,上下文依赖查找。依赖注入有两种类型:构造函数注入和setter注入。下面一一介绍
依赖拉取是最常见的ioc类型,在依赖拉取中,根据需要从注册表中提取依赖项。
Spring还提供依赖拉取作为一种检索框架所管理组件的机制:
public class DependencyPull {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring/app-context.xml");
MessageRenderer mr = applicationContext.getBean("renderer",MessageRenderer.class);
mr.render();
}
}
上下文依赖朝朝在某些方面与依赖拉取类似,查找是针对管理资源的容器执行的,而不是来自某个中央注册表,并且通常在某个设定点执行。
依赖查找通过组件实现以下接口来进行工作,通过实现这个接口,一个组件可以向容器发送它想要获取依赖项的信号。
public interface ManagedComponent {
void performLookup(Container container);
}
public interface Container {
Object getDependency(String key);
}
public class ContextualizedDependencyLookup implements ManagedComponent {
private Depency depency;
public void performLookup(Container container) {
this.depency =(Depency) container.getDependency("myDependency");
}
@Override
public String toString() {
return depency.toString();
}
}
当容器准备将依赖项传递给组件时,会依次调用每个组件的performLookup() 方法,然后组件可以使用container接口查找所需的依赖项。
当在组件的构造函数中提供依赖项时,就会发生构造函数依赖注入。首先生命一个或一组构造函数,并将其依赖项作为参数,然后在组件实例化时由ioc容器将依赖项传递给组件。使用构造函数注入的一个显而易见的结果是,如果没有依赖项就不能创建对象,因此必须有依赖项。
public class ConstructorInjection {
private Dependency dependency;
public ConstructorInjection(Dependency dependency) {
this.dependency = dependency;
}
@Override
public String toString() {
return dependency.toString();
}
}
在setter注入中,ioc容器可以通过setter方法注入组件的依赖项。组件的setter方法公开了ioc容器可以管理的依赖项。使用setter注入的一个显而易见的后果是,可以在没有依赖项的情况下创建对象,然后可以通过调用setter来提供依赖项。实际上,setter注入是使用最广泛的注入机制,也是最简单的ioc机制之一。
public class SetterInjection {
private Dependency dependency;
public void SetterInjection(Dependency dependency) {
this.dependency = dependency;
}
@Override
public String toString() {
return dependency.toString();
}
}
现在问题来了,如果可以选择,应该使用哪种方法,注入还是查找?答案绝对是注入。一方面,注入对组件没有任何影响。另一方面,依赖拉取必须主动获得对注册表的引用并与其交互以获取依赖项。
如果使用注入,则可以自由的使用与ioc容器完全分离的类,而ioc容器通过手动为他们的协作者提供依赖对象;而如果使用查找,那么你的类总是依赖于容器定义的类和接口。查找的另一个缺点是难以独立于容器来测试类,而使用注入则可以非常容易的测试自己的组件,因为可以通过使用适当的构造函数或setter来提供依赖项。
现在问题又来了,我们已经选择使用注入了,但是是选择setter注入还是构造函数注入?答案是灵活使用
当在使用组件之前必须拥有一个依赖类的实例时,构造函数注入就特别有用了,但如果使用构造函数注入,则可以通过一种容器无关的方式声明对依赖项的需求,此外,构造函数注入也有助于实现不可变对象的使用。
setter注入在各种情况下都很有用。如果组件向容器公开了它的依赖项,并乐于提供自己的默认值,那么setter注入通常是实现此目的的最佳方法。setter注入的另一个好处是,它允许在接口上声明依赖项,尽管这种方法并没有前面的方法有用。setter注入还允许即时交换针对不同实现的依赖项,而无须创建父组件的新实例。Spring的JMX支持使这项功能成为可能。也许setter注入的最大好处是,它是注入机制中侵入性最小的。
一般来说,应根据使用情况选择注入类型。基于setter的注入允许在不创建新对象的情况下交换依赖项,并且还可以让类选择适当的默认值,而无须显式注入对象。当想要确保将依赖项传递给组件和设计不可变对象时,构造函数注入是一个不错的选择。
o