@Purpose
2017-06-11T04:18:16.000000Z
字数 3868
阅读 1792
Spring
Spring框架核心是IoC容器,所以学习Spring框架就要先理解控制反转的思想以及依赖注入的实现方法。一开始我觉得这些名词挺高大上,然而在拜读了肖汉松老师的博客:
“控制反转(IoC)与依赖注入(DI)”之后,发现这些但其实是不难理解的,下面我们通过探讨一下问题来认识和学习IoC和DI之间的关系以及在Spring中的应用。
首先,我们来讨论一下耦合这个词。这个词语相信各位都不陌生,下面这幅图就非常形象地描述了在软件开发中耦合发生的情况
在上图,每一个齿轮就代表我们开发中的一个对象,在图中我们可以看到,每个齿轮之间都耦合起来了,彼此依赖,协同工作。但是这样做的问题也非常明显:一旦某个齿轮坏了不能运转,就会导致整个系统的崩溃,这就是强耦合系统的致命的问题。
然而多耦合的情况我们是无法避免的,这是协同工作的基础,而且随着工程应用的规模变大,依赖关系也会变得越来越复杂,很容易就牵一发而动全身。
为了解决这个问题,大师Michael Mattson提出了IoC理论,用来实现对象之间的解耦
控制反转(Inversion of Control)是一种面向对象编程中的一种设计思想,它的基本思想就是:借助于“第三方”实现具有依赖关系的对象之间的解耦。
由于引进了中间位置的“第三方”,也就是IoC容器,使得A、B、C、D这4个对象没有了耦合关系,齿轮之间的传动全部依靠“第三方”了,全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心。
我们再来看看,控制反转(IoC)到底为什么要起这么个名字?我们来对比一下:
其实与其说控制反转是一种编程思想,不如说它是一种管理模式。生活中控制反转,依赖倒置的思想应用其实更为广泛。
海尔公司作为一个电器制商需要把自己的商品分销到全国各地,但是发现,不同的分销渠道有不同的玩法,于是派出了各种销售代表玩不同的玩法,随着渠道越来越多,发现,每增加一个渠道就要新增一批人和一个新的流程,严重耦合并依赖各渠道商的玩法。实在受不了了,于是制定业务标准,开发分销信息化系统,只有符合这个标准的渠道商才能成为海尔的分销商。让各个渠道商反过来依赖自己标准。反转了控制,倒置了依赖。
我们把海尔和分销商当作软件对象,分销信息化系统当作IOC容器,可以发现,在没有之前分销信息化系统,分销商就像图1中的齿轮一样,增加一个齿轮就要增加多种依赖在其他齿轮上,势必导致系统越来越复杂。开发分销系统之后,所有分销商只依赖分销系统,就像图2显示那样,可以很方便的增加和删除齿轮上去。
依赖注入就是将实例变量传入到一个对象中去(Dependency injection means giving an object its instance variables)。
在Class A中,有Class B的一个实例,就叫做Class A 对 Class B 有一个依赖。比如下面的类 Human 中,有一个Man的对象,那么就类 Human 对类 Man有一个依赖。
public class Human {...Man man;...public Human() {man = new Man();}}
我们不难看出以上代码存在的问题:
现在我们改进一下,把两个类进行解耦
public class Human {...Man man;...public Human(Man man) {this.man = man;}}
在上面代码中,类Man 的实例对象 man 是在Human类外构建好的,也就是在调用Human 的构造方法之前,就已经在外部实例化好了Man 对象,而无需Human在自己的内部自行构建。像这种非自己主动初始化依赖,而通过外部来传入依赖的方式,我们就称为依赖注入。
对比这两段代码,我们就能体会到依赖注入的好处:
上面已经分别解释了控制反转和依赖注入的概念。有些人会把控制反转和依赖注入等同,但实际上它们有着本质上的不同。
他们之间的关系有点像类和实例对象之间的关系,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容器。
Spring中使用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");//1MovieLister lister = (MovieLister) ctx.getBean("MovieLister");//2Movie[] movies = lister.moviesDirectedBy("Sergio Leone");assertEquals("Once Upon a Time in the West", movies[0].getTitle());}
ApplicationContext,即IoC容器。MovieLister的实例。原文链接: 肖汉松: 控制反转(IoC)与依赖注入(DI)