文章目录
- Spring 是什么
- 什么是容器
- 什么是 IoC
- IoC 介绍
- 传统程序开发
- 解决方法
- IoC 程序开发
- IoC 的优势
在前面中,我们学习了 Spring Boot 和 Spring MVC 的开发,可以完成一些基本功能的开发了,但是什么是 Spring 呢?Spring,Spring Boot,SpringMVC 又有什么关系呢?
Spring 是什么
通过前面的学习,我们知道了 Spring 是一个开源框架,他让我们的开发更加简单。他支持广泛的应用场景,有着活跃而庞大的社区,这也是 Spring 能后长久不衰的原因
但是这个概念相对来说是比较抽象的。我们用一句更具体的话来概括 Spring
,那就是:Spring
是包含了众多工具方法的 IoC
容器
- 那问题来了,什么是容器?什么是 IoC 容器?
什么是容器
容器是用来容纳某种物品的(基本)装置。生活中的水杯,垃圾桶,冰箱等等这些都是容器。
我们想象,之前的内容中我们接触到了哪些容器?
- List/Map:数据存储容器
- Tomcat:Web 容器
什么是 IoC
IoC
是 Spring
的核心思想,也是常见的面试题。其实我们在前面已经使用过了 IoC
,在类上面添加 @RestController
注解,就是把这个对象交给 Spring
管理,Spring
框架启动时就会加载该类。把对象交给 Spring
管理,就是 IoC
思想
IoC:Inversion of Control(控制反转),也就是说 Spring 是一个“控制反转”的容器
什么是控制反转呢?
也就是控制权反转,获得依赖对象的过程被反转了,也就是说,当需要某个对象时,传统开发模式中需要自己通过 new 创建对象,现在不需要再及你选哪个创建,把创建对象的任务交给容器,程序中只需要依赖注入(Dependency Injection,DI)就可以了,这个容器称为:IoC 容器,Spring 是一个 IoC 容器,所以有时 Spring 也称为 Spring 容器
控制反转是一种思想,在生活中也是处处体现
比如自动驾驶,传统驾驶方式,车辆的横向和纵向驾驶控制权由驾驶员来控制,现在交给了驾驶自动化系统来控制,可也是控制反转思想在生活动的实现
比如招聘,企业的员工招聘,入职,解雇等控制权,由老板转交给 HR 来处理
IoC 介绍
需求:造一台车
传统程序开发
我们的实现思路是这样的:
先设计轮子(Tire),然后根据轮子的大小设计底盘(Bottom),接着根据底盘设计车身(Framework),最后根据车身设计好整个汽车(Car)。这里就出现了一个“依赖”关系:汽车依赖车身,车身依赖底盘,底盘依赖轮子
public class NewCarExample {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("framework init..."); } public void run() { System.out.println("car run..."); } }/***车身类*/static class Framework { private Bottom bottom; public Framework() { bottom = new Bottom(); System.out.println("bottom init..."); } } /***底盘类*/static class Bottom { private Tire tire; public Bottom() { tire = new Tire(); System.out.println("tire init..."); } }/***轮胎类*/public class Tire { private int size = 21; public Tire() { System.out.println("tire size:" +size); } }
}
这样的设计看起来没问题,但是可维护性却很低.
接下来需求有了变更: 随着对的⻋的需求量越来越⼤, 个性化需求也会越来越多,我们需要加⼯多种尺
⼨的轮胎.
那这个时候就要对上⾯的程序进⾏修改了,修改后的代码如下所⽰
修改之后, 其他调⽤程序也会报错, 我们需要继续修改
完整代码如下:
public class NewCarExample {public static void main(String[] args) {Car car = new Car(21);car.run(); }/***汽车对象*/static class Car { private Framework framework; public Car(int size) { framework = new Framework(size); System.out.println("framework 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("bottom init..."); } } /***底盘类*/static class Bottom { private Tire tire; public Bottom(int size) { tire = new Tire(size); System.out.println("tire init..."); } }/***轮胎类*/public class Tire { private int size = 21; public Tire(int size) { this.size = size;System.out.println("tire size:" +size); } }
}
从以上代码可以看出,以上程序的问题是:当最底层代码改动之后,整个调⽤链上的所有代码都需要
修改.
程序的耦合度⾮常⾼(修改⼀处代码, 影响其他处的代码修改)
解决方法
在上⾯的程序中, 我们是根据轮⼦的尺⼨设计的底盘,轮⼦的尺⼨⼀改,底盘的设计就得修改. 同样因
为我们是根据底盘设计的⻋⾝,那么⻋⾝也得改,同理汽⻋设计也得改, 也就是整个设计⼏乎都得改
我们尝试换⼀种思路, 我们先设计汽⻋的⼤概样⼦,然后根据汽⻋的样⼦来设计⻋⾝,根据⻋⾝来设计底盘,最后根据底盘来设计轮⼦. 这时候,依赖关系就倒置过来了:轮⼦依赖底盘, 底盘依赖⻋⾝,⻋⾝依赖汽⻋
这就类似我们打造⼀辆完整的汽⻋, 如果所有的配件都是⾃⼰造,那么当客⼾需求发⽣改变的时候,
⽐如轮胎的尺⼨不再是原来的尺⼨了,那我们要⾃⼰动⼿来改了,但如果我们是把轮胎外包出去,那
么即使是轮胎的尺⼨发⽣变变了,我们只需要向代理⼯⼚下订单就⾏了,我们⾃⾝是不需要出⼒的.
如何来实现呢:
我们可以尝试不在每个类中⾃⼰创建下级类,如果⾃⼰创建下级类就会出现当下级类发⽣改变操作,
⾃⼰也要跟着修改.
此时,我们只需要将原来由⾃⼰创建的下级类,改为传递的⽅式(也就是注⼊的⽅式),因为我们不
需要在当前类中创建下级类了,所以下级类即使发⽣变化(创建或减少参数),当前类本⾝也⽆需修
- 改任何代码,这样就完成了程序的解耦
IoC 程序开发
基于以上思路,我们把调用汽车的程序实例改造一下,把创建子类的方式,改为注入传递的方式,具体实现代码如下:
public class NewCarExample {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(framewoek);car.run(); }/***汽车对象*/static class Car { private Framework framework; public Car(Framework framework) { framework = new Framework(); System.out.println("framework init..."); } public void run() { System.out.println("car run..."); } }/***车身类*/static class Framework { private Bottom bottom; public Framework(Bottom bottom) { bottom = new Bottom(); System.out.println("bottom init..."); } } /***底盘类*/static class Bottom { private Tire tire; public Bottom(Tire tire) { tire = new Tire(); System.out.println("tire init..."); } }/***轮胎类*/public class Tire { private int size = 21; public Tire(int size) { this.size = size;System.out.println("tire size:" +size); } }
}
- 代码经过以上调整,⽆论底层类如何变化,整个调⽤链是不⽤做任何改变的,这样就完成了代码之间的解耦,从⽽实现了更加灵活、通⽤的程序设计了。
IoC 的优势
- 在传统的代码中对象创建顺序是:
Car
->Framework
->Bottom
->Tire
- 改进之后解耦的代码的对象创建顺序是:
Tire
->Bottom
->Framework
->Car
我们发现了⼀个规律,通⽤程序的实现代码,类的创建顺序是反的,传统代码是 Car
控制并创建了 Framework
,Framework
创建并创建了 Bottom
,依次往下,⽽改进之后的控制权发⽣的反转,不再是使⽤⽅对象创建并控制依赖对象了,⽽是把依赖对象注⼊将当前对象中,依赖对象的控制权不再由当前类控制了.
这样的话, 即使依赖类发⽣任何改变,当前类都是不受影响的,这就是典型的控制反转,也就是 IoC
的实现思想。
学到这⾥, 我们⼤概就知道了什么是控制反转了, 那什么是控制反转容器呢, 也就是 IoC
容器
这部分代码, 就是 IoC
容器做的⼯作.
从上⾯也可以看出来, IoC
容器具备以下优点:
- 资源不由使⽤资源的双⽅管理,⽽由不使⽤资源的第三⽅管理,这可以带来很多好处。第⼀,资源集中管理,实现资源的可配置和易管理。第⼆,降低了使⽤资源双⽅的依赖程度,也就是我们说的耦合度。
-
资源集中管理:
IoC
容器会帮我们管理⼀些资源(对象等), 我们需要使⽤时, 只需要从IoC
容器中去取
就可以了 -
我们在创建实例的时候不需要了解其中的细节, 降低了使⽤资源双⽅的依赖程度, 也就是耦合度。
Spring
就是⼀种IoC
容器, 帮助我们来做了这些资源管理.