我们通常所说的 Spring
指的是 Spring Framework
(Spring 框架),它是⼀个开源框架,有着活跃⽽庞⼤的社区,这就是它之所以能⻓久不衰的原因.Spring ⽀持⼴泛的应⽤场景,它可以让 Java 企业级的应⽤程序开发起来更简单.
⽤⼀句话概括 Spring: Spring 是包含了众多⼯具⽅法的IoC容器.
因为学习框架相当于从“小作坊”到“工厂”的升级,小作坊什么都要自己做,工厂是组件式装配,特点就是高效。框架更加易⽤,简单且高效.
Servlet有以下痛点:
而 Spring Boot 相比于 Servlet 具备以下优点:
Spring框架基本学习应用路线:Spring全家桶
(Spring/Spring Boot/Spring MVC) -> MyBatis
-> Redis
等。
Spring 核心就是这么一句话:Spring 框架是包含了众多工具方法的 IoC 容器。
那么这句话怎么理解呢?什么是容器?什么又是 IoC?
容器是⽤来容纳某种物品的(基本)装置。 ——来⾃:百度百科
之前我们接触的容器有以下:
Spring 是就一个 IoC
容器,它包含了许多的工具和方法.
IoC : Inversion of Control
翻译成中⽂是“控制反转”的意思.这是一种思想,控制权反转,在 Java 的常规代码中,对象的生命周期,是由当前代码(程序员自己)控制的,而控制权反转就是对象的生命周期,不再由当前代码片段来控制,而是由 Spring
(IoC 容器)来控制.
举个:
我们都知道汽车包含轮胎,底盘,车身等.现在我们要造一辆汽车,需要有车身,而车身需要依赖于底盘.而底盘依赖于轮胎.
传统的思想中,在造车的时候需要车身,于是就new
一个车身,而车身需要底盘,于是就再new
一个底盘,底盘需要轮胎,于是就再new
一个轮胎.
package oldCar;
//传统汽车制造
//汽车类
public class Car {
//车身
private Framework framework;
public Car(){
framework = new Framework();
}
public void init() {
System.out.println("do car");
// 汽车的组建依赖于车身
framework.init();
}
}
package oldCar;
//车身类
public class Framework {
//底盘
private Bottom bottom;
public Framework(){
bottom = new Bottom();
}
public void init() {
System.out.println("do framework");
// 车身的组建依赖于底盘
bottom.init();
}
}
package oldCar;
//底盘类
public class Bottom {
//车轮
private Tire tire;
public Bottom(){
tire = new Tire();
}
public void init(){
System.out.println("do Bottom");
//底盘依赖于轮胎
tire.init();
}
}
package oldCar;
//轮胎类
public class Tire {
private int size = 17;//轮胎的尺寸
public void init(){
System.out.println("size -> " + size);
}
}
package oldCar;
public class Test {
public static void main(String[] args) {
Car car = new Car();
car.init();
}
}
车轮为17寸的汽车制作完毕.但是上述代码中轮胎大小是写死的,随着时代发展,人们对车的需求量越来越大,个性化需求也会越来越多,这时候我们就需要加工多种尺寸的轮胎,此时就需要修改上述代码.我们需要给轮胎Tire
类的构造方法加上一个参数.由于底盘Bottom
类控制了Tire
类,那么底盘类的构造方法也需要加上一个参数.以此类推,Freamwork
和Car
类都需要在构造方法中加上一个参数.
package oldCar;
//传统汽车制造
//汽车类
public class Car {
//车身
private Framework framework;
public Car(int size){
framework = new Framework(size);
}
public void init() {
System.out.println("do car");
// 汽车的组建依赖于车身
framework.init();
}
}
package oldCar;
//传统汽车制造
//汽车类
public class Car {
//车身
private Framework framework;
public Car(int size){
framework = new Framework(size);
}
public void init() {
System.out.println("do car");
// 汽车的组建依赖于车身
framework.init();
}
}
package oldCar;
//传统汽车制造
//汽车类
public class Car {
//车身
private Framework framework;
public Car(int size){
framework = new Framework(size);
}
public void init() {
System.out.println("do car");
// 汽车的组建依赖于车身
framework.init();
}
}
package oldCar;
//轮胎类
public class Tire {
private int size = 17;//轮胎的尺寸
public Tire(int size) {
this.size = size;
}
public void init(){
System.out.println("size -> " + size);
}
}
package oldCar;
public class Test {
public static void main(String[] args) {
Car car = new Car(20);
car.init();
}
}
车胎尺寸为20寸的汽车制作好了.此时如果需要定制不同轮胎大小的尺寸,只需要改动狗仔Cae对象传入的参数即可.但是,如果再加上一个需求,改变车身颜色,那我们又要加三叔,此时就意味着和上述代码一样修改每一层的代码,整个调用链上的代码都需要修改.
上述代码耦合度就太高了.为了解决这个问题,我们可以使用loC
的思想来实现代码,将控制权交出去,也就是说,IoC
模式下,我们不再自己构造创建对象.当我们需要轮胎Tire
类时,你就给我传一个Tire
对象,我们不去new一个Tire对象了,这样的话,就算在Tire
类加参数也只需要改动Tire类的构造方法与相关执行方法与属性,顶多再改一下Tire对象的创建,同理其他类也一样,将对象作为参数传入到上级类的构造方法中去就行了,但此时其他类是不需要修改的,这个过程也叫做传入或注入。
由于我们创建Car时需要Framework,所以先要实例一个Framework对象,同理实例一个Framework对象需要Bottom对象,那么需先实例一个Bottom对象,一样,在实例Bottom对象之前需要实例一个Tire对象,于是需要先后创建Tire对象,Bottom对象,Framework对象后才能创建一个Car对象,我们可以得到如下的代码:
package newCar;
public class Car {
// 汽车的组建依赖于车身的组建
private Framework franmework;
public Car(Framework franmework) {
this.franmework = franmework;
}
public void init() {
System.out.println("do car...");
franmework.init();
}
}
package newCar;
public class Framework {
// 车身的组建依赖于底盘
private Bottom bottom;
public Framework(Bottom bottom) {
this.bottom = bottom;
}
public void init() {
System.out.println("do franmework");
bottom.init();
}
}
package newCar;
public class Bottom {
// 底盘的组建依赖于轮胎
private Tire tire;
public Bottom(Tire tire) {
this.tire = tire;
}
public void init() {
System.out.println("do bottom...");
tire.init();
}
}
package newCar;
public class Tire {
private int size = 17;
private String color = "黑色";
public Tire(int size, String color) {
this.size = size;
this.color = color;
}
public void init() {
System.out.println("size -> " + size);
System.out.println("color->" + color);
}
}
package newCar;
public class IOCDemo {
// 这里的内容包含就相当于是 IoC 容器做的事情
// 对象的生命周期控制权就翻转给 IoC 容器了, 不再由程序员控制
private Tire tire;
private Bottom bottom;
private Framework framework;
public Car car;
public IOCDemo() {
tire = new Tire(17, "红色");
bottom = new Bottom(tire);
framework = new Framework(bottom);
car = new Car(framework);
}
}
package newCar;
/**
* 模拟 IoC 容器
*/
public class Test {
public static void main(String[] args) {
// 直接使用, 创建就交给IoC了
IOCDemo ioc = new IOCDemo();
Car car = ioc.car;
car.init();
}
}
此时如果要变需求,需要加参数或减少参数,IoC 的代码只需改动两处代码即可, 整个调用链是不用做任何改变的, 达到了解耦的目的。
在传统的代码中对象创建顺序是:Car -> Framework -> Bottom -> Tire
改进之后解耦的代码的对象创建顺序是:Tire -> Bottom -> Framework -> Car
到这里我们就可以发现,传统的代码类创建顺序是反着的,Car 控制 FrameWork,FrameWork 控制着 Bottom,Bottom 控制着 Tire;而改进之后的控制权发生了反转,是下层将对象注入到当前对象当中,下级的控制权不再由上级控制了,下级在发生改变时会将改变完成后的对象注入上级,这样上级就是不受影响的,这就是 IoC 的实现思想。
所以 IoC 有以下的优点:对象(Bean)的生命周期交给 IoC 框架维护,作为程序员无需关注,说白了就是程序员不需要关注对象创建、销毁时机以及对象的依赖关系,这些工作加个 IoC 框架(也就是 Spring)做就行,实现了代码的解耦,对象的使用更加方便高效。
Spring 框架就是包含了多个工具方法的 IoC
容器,既然是容器,那它就有存和取的功能,这也是 Spring 最核心的两个功能:
将对象存放到容器有什么好处呢?
将对象存储到 IoC 容器相当于我们将所有可能用到的工具制作好都放到仓库,当我们需要使用时直接取即可,用完归还仓库;而 new 对象的方式相当于我们每次需要用工具的时候现场制作,制作完了扔掉,下次需要的时候重新做。
Spring 是⼀个 IoC 容器,说的是对象的创建和销毁的权利都交给 Spring 来管理了,它本身⼜具备了存储对象和获取对象的能力。
DI,即Dependency Injection
,依赖注入。
所谓依赖注⼊,就是由 IoC 容器在运行期间,动态地将某种依赖关系注入到对象之中,在pom.xml
有一个依赖项,就是用来导入外部的资源,而这里的依赖注入,导入的不是外部的资源,而是对象;当某个 Java 实例需要另一个 Java 实例时,创建被调用者的工作不是由调用者实现,而是由 Spring 容器来完成,然后注入调用者,因此称为依赖注入。
IoC 与 DI 的区别是什么?
依赖注入(DI)和控制反转(IoC)是从不同的角度的描述的同⼀件事情,就是指通过引入 IoC 容器,利用依赖关系注入的方式,实现对象之间的解耦。
IoC 是“目标”也是⼀种思想,而目标和思想只是⼀种指导原则,最终还是要有可行的落地方案,而 DI 就属于具体的实现;也就是说,IoC 是一种思想,而 DI 是 IoC 的一种实现。
DL,即Dependency Lookup
,依赖查找,也是 IoC的一种实现。
依赖查找和依赖注入的区别在于,依赖注入是将依赖关系委托给容器,由容器来管理对象之间的依赖关系,容器外部是不关心这种依赖关系的,需要时由容器判断提供什么;而依赖查找是由对象自己来查找它所依赖的对象,容器只负责管理对象的生命周期,也就是说此时需要容器外部自己确定要容器提供哪种依赖关系;两者之间是主动和被动的区别。