Spring 核心和设计思想
- 1.什么是 Spring
- 1.1 传统程序开发
- 1.2 控制反转程序开发
- 2.理解 Spring IoC
1.什么是 Spring
我们通常所说的 Spring 指的是 Spring Framework(Spring 框架),它是⼀个开源框架,有着活跃而庞大的社区,这就是它之所以能⻓久不衰的原因。Spring 支持广泛的应⽤场景,它可以让 Java 企业级的应⽤程序开发起来更简单。
⽤⼀句话概括 Spring:Spring 是包含了众多⼯具⽅法的 IoC 容器。
由此引发的问题是:什么是容器? 什么又是 IoC 容器呢?
- 什么是容器?容纳某种物品的装置。比如数据结构中的 List / Map(容纳数据)等等,包括 Tomcat(Web 容器)。
- 什么是 IoC 呢?IoC 实际上是 控制反转(
Inversion of Control
)的意思。
如何理解控制反转?
1.1 传统程序开发
假设我们去构建一辆车,利用传统的程序思想进行开发。
构建⼀辆 车(Car Class
),然⽽⻋需要依赖 ⻋身(Framework Class
),⽽⻋身需要依赖 底盘(Bottom Class
),⽽底盘需要依赖 轮胎(Tire Class
),最终程序的实现代码如下。
package com.pp.reflection;
public class NewCarExample {
public static void main(String[] args) {
Car car = new Car();
car.init();
}
/**
* 汽车对象
*/
static class Car {
public void init() {
// 依赖车身
Framework framework = new Framework();
framework.init();
}
}
/**
* 车身类
*/
static class Framework {
public void init() {
// 依赖底盘
Bottom bottom = new Bottom();
bottom.init();
}
}
/**
* 底盘类
*/
static class Bottom {
public void init() {
// 依赖轮胎
Tire tire = new Tire();
tire.init();
}
}
/**
* 轮胎类
*/
static class Tire {
// 尺寸
private int size = 30;
public void init() {
System.out.println("轮胎尺寸:" + size);
}
}
}
通过代码我们发现了一些规律,Car 类依赖于 Framework,也就是 new Car
的时候,Framework 也被创建了,并且调用 init
方法,接下来调用的 init
方法又自动去 new Bottom
,因为通过这些类的含义知道,他们之间是相互依赖的,如果你想去建造一辆车,就需要去保证这种依赖关系。
但是这种设计思想也有缺陷,当你满足不同客户的需求的时候,轮胎的尺寸不同,你就要去修改参数,又或者你想加别的参数,可能解决方法是,在每个类中加上参数,通过参数传递就行了呀。实际上这种方式问题很大,因为这几个类的依赖性很强,我们不断的因为客户的需求而修改类的代码是很麻烦而且很容易出 Bug 的行为。
传统程序开发的缺陷:以上程序中,轮胎的尺⼨的固定的,然⽽随着对的⻋的需求量越来越⼤,个性化需求也会越来越多,这时候我们就需要加⼯多种尺⼨的轮胎,那这个时候就要对上⾯的程序进⾏修改了。
package com.pp.reflection;
public class NewCarExample2 {
public static void main(String[] args) {
Car car = new Car();
car.init(50, "猛男粉");
}
/**
* 汽车对象
*/
static class Car {
public void init(int size, String color) {
// 依赖车身
Framework framework = new Framework();
framework.init(size, color);
}
}
/**
* 车身类
*/
static class Framework {
public void init(int size, String color) {
// 依赖底盘
Bottom bottom = new Bottom();
bottom.init(size, color);
}
}
/**
* 底盘类
*/
static class Bottom {
public void init(int size, String color) {
// 依赖轮胎
Tire tire = new Tire();
tire.init(size, color);
}
}
/**
* 轮胎类
*/
static class Tire {
// 尺寸
// private int size = 30;
public void init(int size, String color) {
System.out.println("轮胎尺寸:" + size + " | 颜色:" + color);
}
}
}
这种就是不断的修改类中的代码,实际上并不能完全解决问题反而会出很大的问题。
从以上代码可以看出,以上程序的问题是:当最底层代码改动之后,整个调⽤链上的所有代码都需要修改。
我们可以尝试不在每个类中⾃⼰创建下级类,如果⾃⼰创建下级类就会出现当下级类发⽣改变操作,⾃⼰也要跟着修改。此时,我们只需要将原来由⾃⼰创建的下级类,改为传递的⽅式(也就是 注入 的⽅式),因为我们不需要在当前类中创建下级类了,所以下级类即使发⽣变化(创建或减少参数),当前类本身也⽆需修改任何代码,这样就完成了程序的解耦。解决传统开发中的缺陷。
🚀 解耦 指的是解决了代码的耦合性,耦合性也可以换⼀种叫法,叫程序相关性。好的程序代码的耦合性(代码之间的相关性)是很低的,也就是代码之间要实现解耦。
实际上 IoC 就是解耦操作。
这就好⽐我们打造⼀辆完整的汽⻋,如果所有的配件都是⾃⼰造,那么当客户需求发⽣改变的时候, ⽐如轮胎的尺⼨不再是原来的尺⼨了,那我们要⾃⼰动⼿来改了,但如果我们是把轮胎外包出去,那么即使是轮胎的尺⼨发⽣改变了,我们只需要向代理⼯⼚下订单就⾏了,我们⾃身是不需要出⼒的。
1.2 控制反转程序开发
基于上述思路,之前我们是通过在构造方法中创建依赖类的方法。现在换一种思路就是改为 依赖注入 的方法。
package com.pp.reflection;
public class IocCarExample {
public static void main(String[] args) {
Tire tire = new Tire(50, "红色");
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;
}
public void run() {
framework.init();
}
}
static class Framework {
private Bottom bottom;
public Framework(Bottom bottom) {
this.bottom = bottom;
}
public void init() {
bottom.init();
}
}
static class Bottom {
private Tire tire;
public Bottom(Tire tire) {
this.tire = tire;
}
public void init() {
tire.init();
}
}
static class Tire {
private int size;
private String color;
public Tire(int size, String color) {
this.size = size;
this.color = color;
}
public void init() {
System.out.println("轮胎:" + size + " | 颜色:" + color);
}
}
}
仔细观察这种代码跟上面的有什么区别?这是很重要的一点。
当我们 new Car
的时候,已经创建好了 framework
,并将 framework
注入到 car
的构造方法中,并且 car.run
中去执行 framework
的 init
方法。每个类的 init
方法实际上都是去执行下一个类的 init
方法。
这种代码是很精妙的,解决了前面的问题。无论我们怎么去改或者加多少个参数,都是很简单的事情,只需要在 new Tire
那里和 Tire
类本身进行修改即可。这就是 IoC 思想。
接下里的两张图可以进行对比一下。
我们发现了⼀个规律:通⽤程序的实现代码,类的创建顺序是反的,传统代码是 Car 控制并创建了 Framework,Framework 创建并创建了 Bottom,依次往下。⽽改进之后的控制权发⽣的反转,不再是上级对象创建并控制下级对象了,⽽是下级对象把注⼊将当前对象中,下级的控制权不再由上级类控制 了,这样即使下级类发⽣任何改变,当前类都是不受影响的,这就是典型的控制反转,也就是 IoC 的实现思想。
在传统设计里面,我们 new
的是 Car,但是改的是 Tire,由于改了 Tire 就要改 Bottom 等等。在 IoC 中,我们通过注入依赖的方式,改的还是 Tire,但是我们已经将 Tire 注入到 Bottom 中去了,所以 Bottom 也随之改变了,之后也是如此,所以他主要是对之前的控制顺序进行了反转,所以 IoC 叫做 控制反转。
IoC 的设计思想说完了,那么如何去理解 Spring 是一个 IoC 容器呢?
2.理解 Spring IoC
既然 Spring 是⼀个 IoC 容器,重点还在 容器 ⼆字上,那么它就具备两个最基础的功能:
- 1️⃣ 将对象存⼊到容器;
- 2️⃣ 从容器中取出对象。
也就是说学 Spring 最核⼼的功能,就是 学如何将对象存⼊到 Spring 中,再从 Spring 中获取对象的过程。
将对象存放到容器中的好处:将对象存储在 IoC 容器相当于将以后可能⽤的所有⼯具制作好都放到仓库中,需要的时候直接取就⾏了,⽤完再把它放回到仓库。⽽ new
对象的⽅式相当于,每次需要⼯具了,才现做,⽤完就扔掉了,也不会保存,下次再⽤的时候还得重新做。这就是 IoC 容器和普通程序开发的区别。
Spring 是⼀个 IoC 容器,说的是 对象的创建和销毁的权利都交给 Spring 来管理了,它本身⼜具备了存储对象和获取对象的能⼒。
说到 IoC 不得不提的⼀个词就是 DI
(Dependency Injection
),即 依赖注⼊。所谓依赖注⼊,就是由 IoC 容器在运⾏期间,动态地将某种依赖关系注⼊到对象之中。所以,依赖注⼊(DI)和控制反转(IoC)是从不同的⻆度的描述的同⼀件事情,就是指通过引⼊ IoC 容器,利⽤依赖关系注⼊的⽅式,实现对象之间的解耦。IoC 是⽬标,也是⼀种思想,⽽⽬标和思想只是⼀种指导原则,最终还是要有可⾏的落地⽅案,⽽ DI 就属于具体的实现。DI 就是我们讲的依赖注入的实现,通过将下级类注入进去的具体实现。
所以建议大家多去看看这种通过注入方式来实现的代码,这对我们如何去设计代码是很重要的。