目录
1.什么是IoC?
IoC是Spring的核心思想,也是常见的面试题,那么什么是IoC呢?
其实IoC前面已经使用过了,我们在前面讲到,在类上面添加 @RestController 和 @Controller 注解,就是把这个对象交给Spring管理,Spring框架启动时就会加载该类.把对象交给Spring管理,就是IoC思想.
IoC: Inversion of Controller(控制反转),也就是说Spring是一个"控制反转"的容器.
什么是控制反转呢? 也就是控制权反转.什么的控制权发生了反转呢?获得依赖对象的过程被反转了,也就是说,当需要某个对象时,传统开发模式中需要自己通过new创建对象,现在不需要再进行创建,把创建对象的任务交给容器,程序中只需要依赖注入(Dependency Injection,DI)就可以了.
这个容器称为: IoC容器.Spring是一个IoC容器,所以有时候Spring 也称为Spring容器.
1) IoC介绍
接下来我们通过案例来了解一下什么是IoC
需求: 造一辆车
(1).传统程序开发
我们实现的思路是这样的:
先设计轮子(Tire),然后根据轮子的大小设计地盘(Bottom),接着根据地盘设计车身(Framework),最后根据车身设计好整个汽车(Car).这里就出现了一个"依赖关系" : 汽车依赖车身,车身依赖地盘,地盘依赖轮子.
最终程序的实现代码如下:
public class NewCarExaple {
public static void main(String[] args) {
Car car = new Car();
car.run();
}
/*
* 汽车对象
* */
static class Car {
private Framework frameWork;
public Car() {
frameWork = new Framework();
System.out.println("Car init...");
}
public void run() {
System.out.println("Car run...");
}
}
/*
* 车身类
* */
static class Framework {
private Bottom bottom;
public Framework() {
bottom = new Bottom();
System.out.println("Framework init...");
}
}
/*
* 底盘类
* */
static class Bottom {
private Tire tire;
public Bottom() {
this.tire = new Tire();
System.out.println("Bottom init...");
}
}
/*
* 轮胎类
* */
static class Tire {
private int size;
public Tire() {
this.size = 17;
System.out.println("轮胎尺寸: " + size);
}
}
}
问题分析
这样设计看起来没有问题,但是可维护性却很低
接下来需求有了变更: 随着对车的需求量越来越大,个性化需求也会越来越多,我们需要加工多种尺寸的轮胎.
那这个时候就要对上面的程序进行修改了,修改后的代码如下所示:
public class NewCarExaple {
public static void main(String[] args) {
Car car = new Car(20);
car.run();
}
/*
* 汽车对象
* */
static class Car {
private Framework frameWork;
public Car(int size) {
frameWork = new Framework(size);
System.out.println("Car init...");
}
public void run() {
System.out.println("Car run...");
}
}
/*
* 车身类
* */
static class Framework {
private Bottom bottom;
public Framework(int size) {
bottom = new Bottom(size);
System.out.println("Framework init...");
}
}
/*
* 底盘类
* */
static class Bottom {
private Tire tire;
public Bottom(int size) {
this.tire = new Tire(size);
System.out.println("Bottom init...");
}
}
/*
* 轮胎类
* */
static class Tire {
private int size;
public Tire(int size) {
this.size = 17;
System.out.println("轮胎尺寸: " + size);
}
}
}
从上面的代码可以看出, 以上程序的问题: 当最底层代码改动之后,整个调用链上的所有代码都需要修改.
程序的耦合度非常高.
解决方案
在上面程序中,我们是根据轮子的尺寸设计的地盘,轮子的尺寸一改,底盘的设计就得修改.同样因为我们是根据地盘设计的车身,那么车身也得修改,同理汽车设计也得改,也就是整个设计几乎都得修改
我们尝试换一种思路,我们先设计汽车的大概样子,然后根据汽车的样子来设计车身,根据车身来设计地盘,最后根据地盘来设计轮子,这时候,依赖关系就倒置了: 轮子依赖地盘,地盘依赖车身,车身依赖汽车.
IoC程序开发
基于以上思路,我们把调用汽车的程序示例改造一下,把创建子类的方式,改为注入的方式,具体代码如下:
public class IocCarExample {
public static void main(String[] args) {
Tire tire = new Tire(20);
Bottom bottom = new Bottom(tire);
Framework framework = new Framework(bottom);
Car car = new Car(framework);
car.run();
}
/*
* 汽车对象
* */
static class Car {
private Framework frameWork;
public Car(Framework framework) {
this.frameWork = framework;
System.out.println("Car init...");
}
public void run() {
System.out.println("Car run...");
}
}
/*
* 车身类
* */
static class Framework {
private Bottom bottom;
public Framework(Bottom bottom) {
this.bottom = bottom;
System.out.println("Framework init...");
}
}
/*
* 底盘类
* */
static class Bottom {
private Tire tire;
public Bottom(Tire tire) {
this.tire = tire;
System.out.println("Bottom init...");
}
}
/*
* 轮胎类
* */
static class Tire {
private int size;
public Tire(int size) {
this.size = size;
System.out.println("轮胎尺寸: " + size);
}
}
}
代码经过以上调整,无论底层类如何变化,整个调用链是不用做任何改变的,这样就完成了代码之间的解耦,从而实现了更加灵活,通用的程序设计了.
IoC的优势
在传统代码中创建对象顺序是: Car->Framework->Bottom->Tire
改进之后的解耦的代码的对象创建顺序是:Tire->Bottom->Framework->Car
我们发现一个规律,通用程序的实现代码,类的创建顺序是反的,传统代码是Car控制并创建了
Framework,Framework创建并创建了Bottom,依次往下,而改进之后的控制权发生的反转,不再是使用方对象创建并控制依赖对象了,而是把依赖对象注入当前对象中,依赖对象的控制权不在由当前类控制了.
这样的话,即使依赖类发生任何改变,当前类都是不受影响的,这样是典型的控制反转,也就是IoC的实现思想.
学到这里,我们大概知道了什么是控制反转了,那么什么是控制反转呢,也就是IoC容器
这部分代码,就是IoC容器做的工作.
从上面也可以看出,IoC容器具备以下优点:
资源不由使用资源的的双方管理,而由不使用资源的第三方管理,这可以带来很多好处.第一, 资源集中管理,实现资源的可配置和易管理.第二, 降低了使用资源双方的依赖程度,也就是我们说的耦合度.
1.资源集中管理: IoC容器会帮我们管理一些资源(对象等),我们需要使用时,只需要从IoC容器中去取就可以了.
2.我们在创建实例的时候不需要了解其中的细节,降低了使用资源双方的依赖程度,也就是耦合度.
Spring就是一种IoC容器,帮助我们来做了这些资源管理.
DI介绍
上面学习了IoC,什么时DI呢?
容器在运行期间,动态的为应用程序提供运行时所依赖的资源,称之为依赖注入,
程序运行时需要某个资源,此时容器就为其提供这个资源.
从这点看,依赖注入(DI)和控制反转(IoC)是从不同的角度描述的同一件事情,就是指通过引入IoC容器,利用依赖关系注入的方式,实现对象之间的耦合.
上述代码中,是通过构造函数的方式,把依赖对象注入到需要使用的对象中的
IoC是一种思想,也是"目标",而思想只是一种指导原则,最终还是要有可行的落地方案,而DI就属于具体的实现,所以也可以说,DI是IoC的一种实现.