Spring源码解读,认识Spring IOC容器
知识要点:
什么是Spring IoC容器
什么是IoC
什么是依赖倒置
为什么需要Spring IoC
什么是Spring IoC容器
Spring Ioc容器的最主要作用就是:完成对象的创建管理和依赖注入等 。
什么是IoC
IoC(Inversion of Control,控制反转)是Spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由Spring来负责控制对象的生命周期和对象之间的关系。
既然提到了IoC,我们就不得不提及设计模式中的六大原则。因为原则之一的“依赖倒置”跟IoC有很大的关系。
控制反转(IoC)是DIP(Dependence Inversion Principle,依赖倒置原则)的一种具体实现方式。 依赖注入(DI,Dependence Injection)是IoC的一种具体实现方式,用来实现依赖倒置。
设计模式六大原则
- 单一原则
一个类或者一个方法只负责一个职责,各个职责的程序改动,不影响其它程序。 - 里氏代换
所有引用父类的地方必须能透明地使用其子类的对象,反过来则不成立。 - 接口隔离
建立单一接口,不要建立庞大臃肿的接口。一个接口只服务于一个子模块或业务逻辑。 - 迪米特原则
一个类对其他的类知道的越少越好,也就是对于被依赖的类的方法和属性尽量私有化。 - 开闭原则
对扩展开放,对修改关闭。 - 依赖倒置
面向接口编程是依赖倒置原则的核心,上层定义接口,下层实现这个接口, 从而使得下层依赖于上层,降低耦合度。
让我们来看一段代码:
class Laptop {
public void start(){
System.out.println("Laptop启动....");
}
public void run(){
System.out.println("Laptop运行....");
}
}
class Programmer {
Laptop laptop = new Laptop();
// 编写代码
public void coding(){
laptop.start();
laptop.run();
}
}
上面这段代码的问题在:如果某一天业务需求要改换成台式电脑(Desktop),这个修改工作肯定是避免不了的。而且工程越复杂修改量越大。怎么处理好呢?
如果我们不进行解耦,它们的关系就会是这样:
而且,在采用面向对象方法设计的软件系统中,底层实现都是由非常多的对象组成,所有的对象通过彼此的合作,最终实现系统的业务逻辑。 随着应用的规模越来越庞大,对象之间的依赖关系也越来越复杂,经常会出现对象之间的多重依赖性关系。这是致命的,不管对于开发还是维护来说。
什么是依赖倒置
如何降低系统之间、模块之间和对象之间的耦合度,是软件工程永远追求的目标之一。为了解决对象之间的耦合度过高的问题,我们使用IoC理论来实现对象之间的“解耦”。
上面的代码,对象和对象之间的耦合太严重。那我们应该如何进行解耦呢?
我们再来看一段代码:
interface Computer {
public void start();
public void run();
}
class Laptop implements Computer{
public void start(){
System.out.println("Laptop启动....");
}
public void run(){
System.out.println("Laptop运行....");
}
}
class Desktop implements Computer{
public void start(){
System.out.println("Desktop启动....");
}
public void run(){
System.out.println("Desktop运行....");
}
}
class Programmer {
Computer c;
Programmer(Computer c){
this.c = c;
}
public void coding(){
c.start();
c.run();
c.stop();
}
// DI 解决依赖问题
// IOC 解决实例化问题
public static void main(String[] args){
Computer c = new Desktop();
Programmer p = new Programmer(p);
p.coding();
}
}
在这段代码里面,就体现了几个设计模式中的原则,当然也包括“依赖倒置”—— 高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
上面的第一段代码中的类是Programmer类依赖了具体Laptop类,如Programmer中的use方法drive,这样Programmer就对具体的Laptop产生了依赖,如果这个时候想要使用其它的工具如Desktop(桌面台式电脑),就需要修改Programmer类。因此我们需要抽象接口Computer,这样Programmer只需要依赖Computer即可,至于到底选择哪种电脑,就交给IOC容器,进行依赖注入即可。
高层模块不应该依赖底层模块
这句话是依赖倒置的核心。指的是概念的自包含,上层模块不应该去依赖具体的某个底层提供方。在设计系统时,常常会采用分层的方式:数据访问层,业务逻辑层,展示层等,而每一层会依赖下层的API,这样导致的问题就是难以替换下层的提供方。那应该如何做呢?业务逻辑层需要有自己的数据访问规范,也就是SPI,然后数据提供层去适配这个SPI,这样如果替换了下层的数据层之后对业务逻辑层也不会产生影响。
抽象不应该依赖细节,细节应该依赖抽象
有了上面的概念之后,自然也就有了依赖抽象而非细节,因为上层制定的是SPI(Service Provider Interface), 也就是一种通用的实现接口,由不同的实现方来提供实现。另外一般实现方也会提供自己的API接口供其他应用来使用,因此一般实现方会在自己的API上面封装一层SPI的适配层来提供给上层使用。
依赖抽象而非细节也是依赖注入与IOC实现的基础。
为什么需要Spring IoC
IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
IOC理论提出的观点大体是这样的:借助于“第三方”实现具有依赖关系的对象之间的解耦,如下图:
在Spring中通过IoC容器来创建这些对象,即由Ioc容器来控制对象的创建,控制外部资源获取(如:对象或资源文件等)。而控制反转在Spring中的体现就是:由容器来创建及注入依赖对象,对象只是被动的接受依赖对象,获取依赖对象的方式也被反转( 与反转相反的做法:通常由我们自己在对象中直接获取依赖对象 )。
总结:IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。