Spring编程思想一依赖注入
一 为什么要使用依赖注入
1.1 一个糟糕的例子
我们知道当一个软件项目复杂到一定程度的时候,需要对代码进行模块化,便于管理,测试和阅读。在Java中采用面向对象的思想进行软件设计,实现一个复杂功能或者业务的时候,都会由两个或者多个类组成,不同的类之间相互协作完成特定的业务逻辑。一般来说我们把代码设计成下面的样子
public class Knight {
private RescueQuest rescueQuest;
public Knight() {
this.rescueQuest = new RescueQuest();
}
public void embarkOnQuest() {
rescueQuest.embark();
}
}
每个类负责管理自己需要的协作的对象的引用(Knight管理RescueQuest)。可以看到Knight在它的构造函数里面自行创建了类RescueQuest。这导致Knight和RescueQuest紧密的耦合在一起。这限制了骑士的探险能力。他不能够去完成除了RescueQuest以外的探险。比如杀死一条恶龙,寻找宝藏,拯救公主等等。
1.2 略微改进
如何改进呢?设计模式中有一句话叫“面向抽象编程”,也就是说不要依赖具体的事物,依赖抽象。我们将骑士的探险能力进行抽象,然后通过构造函数注入进来。我们来看一下代码:
public class Knight {
private Quest quest;
public Knight(Quest quest) {
this.quest = quest;
}
public void embarkOnQuest() {
quest.embark();
}
}
对于现在的骑士来说,他可以通过注入不同的Quest,完成杀恶龙,寻找宝藏,拯救公主的任务了。其实也可以理解为将探险能力的控制权交给了外部。以前是在内部控制我需要什么探险能力,现在将控制反转(IoC),交给外部。需要骑士去打恶龙就注入骑士打恶龙的能力。需要骑士去寻找宝藏就注入骑士寻找宝藏的能力。这样骑士就变成了可以适应多种任务的全能骑士。比起以前厉害多了。但是随着能力总类的增多帮骑士注入能力的人员会很辛苦,那么能不能有什么办法可以简化工作呢?
1.3 使用工厂模式简化注入的任务
当我们的骑士的探险能力逐渐增强,探险能力逐渐增多后。创建和注入的过程就会变的非常复杂。我们可以想象军队出去打仗,会有专门的人来负责士兵们的吃饭,睡觉,装备的维护等。为了让我们的骑士能够更加轻松的完成探险任务,他也需要一个类来专门处理Quest的初始化和注入工作。我们把这个类叫QuestFactory,一个专门生产和装配Quest的工厂。
public class QuestFactory {
public Quest create(String type) {
switch (type) {
case DRAGON:
return new DragonQuest();
case QUEUE:
return new QueueQuest();
case TREASURE:
return new QueueQuest();
default:
return new CommonQuest();
}
}
}
将依赖(Quest)的创建归到工厂内部,使依赖着不用关心依赖的创建过程,统一到一处方便将来的功能的修改和添加。这样就简化了依赖的创建工作。
1.4 将依赖注入交给框架
通过上面的办法,我们的骑士可以完成各种探险,同时不用考虑后勤的问题。但是我们要维护Quest类,还要根据不同的任务去给骑士注入不能的Quest,还是会很麻烦。所以有什么办法可以偷懒吗?其实想想只要遵循依赖的编程思想,那么依赖生产和注入和过程基本上都是类似的。我们可不可以写一个框架出来,然后可以不用再每次都写功能类似的代码了?答案是可以的。(如果你真的想自己写一个IoC容器(依赖注入框架),可以参考这本书——架构探险:从零开始写Java Web框架)再偷懒下,本着前人栽树后人乘凉的原则,一定有前辈已经写好了类似的框架吧。这就是著名的Spring Framework。
什么是依赖注入
最后要给依赖注入下一个定义吧,引用维基百科的定义如下:
在软件工程中,依赖注入是种实现控制反转用于解决依赖性设计模式。一个依赖关系指的是可被利用的一种对象(即服务提供端) 。依赖注入是将所依赖的传递给将使用的从属对象(即客户端)。该服务是将会变成客户端的状态的一部分。 传递服务给客户端,而非允许客户端来建立或寻找服务,是本设计模式的基本要求。
简单来说的话就是:面向抽象编程,由工厂初始化依赖,将依赖的管理交给外部(框架)。
二 Spring中的依赖注入
在Spring中,交给Spring管理的类,都叫做Bean类。所有Bean类都由Spring的BeanFactory进行初始化。整个项目的依赖的关系通过配置文件和注解声明告诉Spring。Spring完成对Bean对初始化和管理。
使用Spring管理后的Knight类
@Component //Component注解表明这个类会作为组建类,同时告知Spring为这个类创建bean。
public class Knight {
private Quest quest;
@Autowired //通知Spring为Knight自动装填Quest类
public Knight(Quest quest) {
this.quest = quest;
}
public void embarkOnQuest() {
quest.embark();
}
}
三 总结:
Spring的依赖注入对我们写程序真的是一大福音,让我们可以从复杂的依赖关系中解放出来,只关心业务代码。更重要的是深入理解这种编程思想,即是没有框架,自己也可以写出耦合度低,易扩展的代码。