标签:架构 tar context 创建 inf 修改 中间 崩溃 cap
抄自:http://blog.xiaohansong.com/2015/10/21/IoC-and-DI/
找不到比这更清楚明白的了
场景:对象A依赖于对象B
控制反转:
控制反转前:由在类A中初始化B,对象A控制着对象B的初始化和使用,
控制反转后:对象B的初始化在对象A需要时由容器初始化并注入到对象A中,控制权在容器手上。
对象A对对象B的依赖,由主动变成了被动,控制权颠倒过来了,对象A与B解耦
依赖注入:就是将实例变量传入到一个对象中去。非自己主动初始化依赖,而通过外部来传入依赖的方式
为了解决对象间耦合度过高的问题,软件专家Michael Mattson提出了IOC理论,用来实现对象之间的“解耦”。
控制反转(Inversion of Control)是一种是面向对象编程中的一种设计原则,用来减低计算机代码之间的耦合度。其基本思想是:借助于“第三方”实现具有依赖关系的对象之间的解耦。
通过前后的对比,我们不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。
控制反转不只是软件工程的理论,在生活中我们也有用到这种思想。再举一个现实生活的例子:
海尔公司作为一个电器制商需要把自己的商品分销到全国各地,但是发现,不同的分销渠道有不同的玩法,于是派出了各种销售代表玩不同的玩法,随着渠道越来越多,发现,每增加一个渠道就要新增一批人和一个新的流程,严重耦合并依赖各渠道商的玩法。实在受不了了,于是制定业务标准,开发分销信息化系统,只有符合这个标准的渠道商才能成为海尔的分销商。让各个渠道商反过来依赖自己标准。反转了控制,倒置了依赖。
我们把海尔和分销商当作软件对象,分销信息化系统当作IOC容器,可以发现,在没有IOC容器之前,分销商就像图1中的齿轮一样,增加一个齿轮就要增加多种依赖在其他齿轮上,势必导致系统越来越复杂。开发分销系统之后,所有分销商只依赖分销系统,就像图2显示那样,可以很方便的增加和删除齿轮上去。
依赖注入就是将实例变量传入到一个对象中去(Dependency injection means giving an object its instance variables)。
如果在 Class A 中,有 Class B 的实例,则称 Class A 对 Class B 有一个依赖。例如下面类 Human 中用到一个 Father 对象,我们就说类 Human 对类 Father 有一个依赖。
public class Human {
...
Father father;
...
public Human() {
father = new Father();
}
}
仔细看这段代码我们会发现存在一些问题:
上面将依赖在构造函数中直接初始化是一种 Hard init 方式,弊端在于两个类不够独立,不方便测试。我们还有另外一种 Init 方式,如下:
public class Human {
...
Father father;
...
public Human(Father father) {
this.father = father;
}
}
上面代码中,我们将 father 对象作为构造函数的一个参数传入。在调用 Human 的构造方法之前外部就已经初始化好了 Father 对象。像这种非自己主动初始化依赖,而通过外部来传入依赖的方式,我们就称为依赖注入。
现在我们发现上面 1 中存在的两个问题都很好解决了,简单的说依赖注入主要有两个好处:
我们已经分别解释了控制反转和依赖注入的概念。有些人会把控制反转和依赖注入等同,但实际上它们有着本质上的不同。
IoC框架使用依赖注入作为实现控制反转的方式,但是控制反转还有其他的实现方式,例如说ServiceLocator,所以不能将控制反转和依赖注入等同。
上面我们提到,依赖注入是实现控制反转的一种方式。下面我们结合Spring的IoC容器,简单描述一下这个过程。
class MovieLister...
private MovieFinder finder;
public void setFinder(MovieFinder finder) {
this.finder = finder;
}
class ColonMovieFinder...
public void setFilename(String filename) {
this.filename = filename;
}
我们先定义两个类,可以看到都使用了依赖注入的方式,通过外部传入依赖,而不是自己创建依赖。那么问题来了,谁把依赖传给他们,也就是说谁负责创建finder
,并且把finder
传给MovieLister
。答案是Spring的IoC容器。
要使用IoC容器,首先要进行配置。这里我们使用xml的配置,也可以通过代码注解方式配置。下面是spring.xml
的内容
<beans>
<bean id="MovieLister" class="spring.MovieLister">
<property name="finder">
<ref local="MovieFinder"/>
</property>
</bean>
<bean id="MovieFinder" class="spring.ColonMovieFinder">
<property name="filename">
<value>movies1.txt</value>
</property>
</bean>
</beans>
在Spring中,每个bean代表一个对象的实例,默认是单例模式,即在程序的生命周期内,所有的对象都只有一个实例,进行重复使用。通过配置bean,IoC容器在启动的时候会根据配置生成bean实例。具体的配置语法参考Spring文档。这里只要知道IoC容器会根据配置创建MovieFinder
,在运行的时候把MovieFinder
赋值给MovieLister
的finder
属性,完成依赖注入的过程。
下面给出测试代码
public void testWithSpring() throws Exception {
ApplicationContext ctx = new FileSystemXmlApplicationContext("spring.xml");//1
MovieLister lister = (MovieLister) ctx.getBean("MovieLister");//2
Movie[] movies = lister.moviesDirectedBy("Sergio Leone");
assertEquals("Once Upon a Time in the West", movies[0].getTitle());
}
ApplicationContext
,即IoC容器。MovieLister
的实例。标签:架构 tar context 创建 inf 修改 中间 崩溃 cap
原文地址:http://www.cnblogs.com/jerrice/p/7123178.html