spring学习笔记-DI
spring开源框架可以说对于java生态起到了一个基石的作用,而spring的两大核心功能IOC容器和AOP可以说支撑了spring框架的流行,这篇文章主要介绍一下ioc容器的一些核心观点。
一、什么依赖注入:
ioc的英文全称是(Inversion of controll),可以翻译成控制反转,其实就是一种权利的反转,那到底是掌握了什么样的权利呢?我们先了解一下DI(依赖注入)我们通过下面的几个例子来深入的讨论一下。
- 生活小场景:
我们需要发送一封电子邮件,我们一般需要几个步骤:
1、需要一个邮件的编辑器;
2、需要一个语法拼写检查的工具和翻译工具;
3、需要一个客户端发送;
对于万物皆对象的java语言来说,我们好像很难将这一系列操作封装成一个统一的操作,我们试者来分解一下。
客户端:Emailer
public class Emailer{
public void send(String text){
//发送邮件
}
}
但是我们需要在发送邮件以前,进行一个翻译和语法检查,比如我们的客户为美国人,so
翻译并语法检查的SpellChecker
public class SpellChecker{
public String checkAndTranslation(String text){
//翻译并且语法检查。
}
}
现在我们在完成邮件发送整个流程的时候,需要两个对象的关联操作,所以在java语言中我们
可能需要两个实例,所以我们可以说Emailer和SpellChecker是有依赖关系的。万物皆有关联
同样在代码中也体现的淋漓精致。
我们来研究一下这两个对象的依赖关系,首先Emailer必须需要SpellChecker才可以完成发送邮
件的功能,并且SpellChecker必须在Emailer中才能发挥自己的功能,看一下一个总结语句:
Dependency—A specific service that is required by another object to fulfill its function.
Dependent—A client object that needs a dependency (or dependencies) in order to perform
its function.
我相信已经可以很深刻的理解上述的两个总结语句了。
那我们需要将这两个对象依赖起来可以怎么做呢:
- 手动构造方法进行依赖关联:
public class Emailer{
private SpellChecker spellChecker;
public Emailer(){
this.spellChecker=new SpellChecker();
}
public void send(String text){
//发送邮件
}
}
1、在构造方法中新建spellChecker实例,在Emailer初始化实例的时候,自然包含了spellChecker实例。
- 思考一下这种方式的一个弊端:
依赖关系仿佛成了一种寄生关系,两个对象紧紧的耦合在一起,可以说SpellChecker是Emailer内部的一部分了,无法测试无法扩展,甚至当客户变为日本人和法国人的时候,我们甚至一时间不能支持。 - 构造器传入参数的方式
public class Emailer {
private SpellChecker spellChecker;
public void setSpellChecker(SpellChecker spellChecker) {
this.spellChecker = spellChecker; }
}
//Emailer emailer=new Emailer(new SpellChecker());
注意我对代码进行了一点修改,构造器中接受了一个SpellChecker,我们可以在创建Emailer的同时,决定传入什么模式的拼写检查器。好像我们平时大游戏的时候根据boss不同更换不同的装备一样,好像一种把一个对象注入另一个对象之中,我们称之为依赖的注入,也就是经常听见的DI,而这种通过构造方法注入的形式我们称之为constructor injection。
- setter方法进行注入
其实和构造方法异曲同工,通过setter方法将一个对象注入另一个对象,这里就不使用代码演示了。但是这个两种方法有没有什么弊端,我们必须要知道依赖的一个关系,同样我们如果在很多地方都需要依赖于一个相同的对象,我们可能需要很多的重复代码,甚至是一个小的依赖改动,我们也需要很多的代码修改,思考如何才能减轻依赖之间的负担呢,有没有一种生产工具,我们需要什么给我们什么。比如我们需要一个发送邮件的东西,就给我们一个发送邮件的东西,我们只需要调用这个东西,而不需要关心里面的构造如何。 - 工厂模式:核心思想就是把这些复杂的依赖交给一个第三方去做,我们称之为工厂,这个很贴近生活,我们只需要把想法告诉工厂,然后工厂就给我们生产出想要的商品,我们不需要关心这个商品制作中需要的种种原料,就是这个意思。
public class EmailerFactory {
public Emailer newFrenchEmailer() {
return new Emailer(new FrenchSpellChecker());
}
}
上述代码就是委托工厂给我们一个Emailer,包含了一个法语的翻译拼写检查器。我们会发现现在这两个对象,不管是Emailer还是SpellChecker的关系变得干净了很多,没有了谁包含谁的现象,都由工厂帮我们搞定了。
Emailer emailer=new EmailerFactory().newFrenchEmailer()
这样的代码是不是很清晰,我们只需要知道我们需要什么样的东西而选择什么样的工厂。
看起来工厂模式已经很好的解决了手动注入的问题,但是我们需要思考一下有什么弊端吗?在工厂中如果我们维护了很多的依赖,依赖中又依赖的情况,我们在添加一种方法的时候还是需要编写一堆的代码。
- The Service Locator pattern(服务定位模式)
其实这种模式也是一种特殊的工厂模式,下面的代码就是一种服务定位模式:
Emailer emailer = (Emailer) new ServiceLocator().get("Emailer");
可以使用kv的形式,用k拿到对应的一个v,而v就是我们需要的服务;java中常见的服务定位模式JNDI(java name and Directory interface)我们也可以理解为发布一个服务,我们根据需要去订阅,发布的服务的命名标准可以就是一个key。但是这种方式太过依赖于这个key,key的书写错误,会导致错误的对象转化错误。
二、DI vs IOC
"Don’t call us; we’ll call you!" 就像好莱坞挑选演员一样。这种来帮助我们管理这些对象之间的依赖蓝图的我们就称之为依赖注入框架,或者依赖注入器,而spring的IOC容器就是一个依赖注入的框架,也就是说IOC容器和DI可以画上等号。什么样的东西可以称之为使用了DI或者是一个IOC容器。下面我们已经更能理解一下几个术语:
1、Dependency injector:依赖注入器,其实就是一个框架或者一个类库提供依赖注入的方法;
2、Dependency injection:依赖注入就是上述讲述的一种处理依赖关系的方案,也可以说是一种模式。
- spring的IOC就是DI的一个很好的实现框架,也就是说是一个优秀的依赖注入器,其中spring更是将注解,反射,泛型,代理这些技术发挥的更好;但是不是说只有spring的ioc容器使用了DI,其实还有很多框架都是DI的完美实现,这个有兴趣可以查看一下。