目录
一、Spring是什么
二、什么是IoC容器
1、什么是容器
2、什么是IoC
3、Spring IoC
4、DI(依赖注入)
4.1、IoC和DI的区别
5、 DL(依赖查找)
一、Spring是什么
我们通常所说的Spring指的是Spring Framework(Framework的意思就是框架),它是一个开源的框架,有着庞大的社区,这就是它之所以能长久不衰的原因,Spring支持广泛的应用场景,它可以方Java企业级的应用程序开发起来更简单。
用一句话来概述Spring:Spring是包含了众多工具方法的IoC容器。
二、什么是IoC容器
1、什么是容器
容器是用来容纳某种物品的装置。 ——来自:百度百科
我们之前了解的List和Map,他们都是用来装数据的容器,这写容器把我们需要常用到的方法都已经封装好了,我们只需要知道如何使用并且知道使用什么方法可以达到我们想要实现的效果即可。还有Tomcat是一个Web容器。同理Spring就是一个IoC容器。
2、什么是IoC
IoC是Inversion of Control的缩写,直译的意思是"控制翻转"的意思,这是一种思想,实际上IoC表示的意思为"控制权翻转"。之前我们写的代码中对象的生命周期,是由程序员或者当前的代码片段控制的;而控制权翻转就是程序中对象的生命周期不在又程序员或者当前的代码片段控制,而是由Spring(IoC容器)来控制。
下面我们通过一个例子来了解这个IoC容器
1️⃣传统代码的写法
我们都知道构造一个汽车,首先这个车需要依赖于车身(FrameWork),而车身需要依赖于底盘(Bottom),而底盘需要依赖于轮胎(Tire),这就需要我们在写代码的时候在底盘的类中new一个轮胎的对象,在车身的类中new一个底盘的对象,在车的类中需要new一个车身的对象。
我们的车肯定有不同的款式,当我们对车的组成所依赖的对象中的属性做出指定(个性化需求)的时候,一个类添加属性的时候,其他依赖(new)这个类的类,在new对象的时候都需要进行修改。
package old;
//汽车类
public class Car {
private Framework framework;
public Car(){
framework = new Framework();
}
public void init(){
//依赖车身
System.out.println("构造汽车");
framework.init();
}
}
package old;
//车身类
public class Framework {
private Bottom bottom;
public Framework(){
bottom = new Bottom();
}
public void init(){
System.out.println("构建车身");
//依赖底座
bottom.init();
}
}
package old;
//底盘类
public class Bottom {
private Tire tire;
public Bottom(){
Tire tire = new Tire();
}
public void init(){
System.out.println("构建底盘");
//依赖轮胎
tire.init();
}
}
package old;
//轮胎类
public class Tire {
private int size = 20;//轮胎尺寸
public void init(){
System.out.println("轮胎尺寸:"+size);
}
}
package old;
//测试类,用来创建一个汽车对象
public class Test {
public static void main(String[] args) {
Car car = new Car();
car.init();
}
}
当我们在创建这个车对象时想要这个车的轮胎尺寸小一点,这个时候就需要我们对轮胎类中的构造方法进行改造,让我们在创建车这个对象的时候,可以直接直接指定车的轮胎大小,如果想修改车的颜色,我们就需要在车身类中添加一个颜色的属性,但是这个构造方法改变之后,对应的之后的类中new的对象的操作都选要进行改变。
package old;
public class Car {
private Framework framework;
public Car(int size,String color){
framework = new Framework(size,color);
}
public void init(){
System.out.println("构造汽车");
//依赖车身
framework.init();
}
}
package old;
public class Framework {
private Bottom bottom;
public String color ;
public Framework(int size,String color){
bottom = new Bottom(size);
this.color = color;
}
public void init(){
System.out.println("车身颜色:"+color);
//依赖底座
bottom.init();
}
}
package old;
public class Bottom {
private Tire tire;
public Bottom(int size){
tire = new Tire(size);
}
public void init(){
System.out.println("构建底盘");
//依赖轮胎
tire.init();
}
}
package old;
public class Tire {
private int size = 20;
public Tire(int size){
this.size = size;
}
public void init(){
System.out.println("轮胎尺寸:"+size);
}
}
package old;
public class Test {
public static void main(String[] args) {
Car car = new Car(12,"red");
car.init();
}
}
可以看见上述的代码是"牵一发而动全身",当最底层代码改动之后,整个调用链上的所有代码都需要改动,这就是代码中的耦合性太高而导致的。
2️⃣使用IoC思想实现的代码
解决这个问题,我们可以使用IoC的思想来实现代码,我们只需要将原来自己创建的下级类,改为传递的方式(也就是注入的方式),我们在上级类中用构造方法的参数来申明这个类需要使用它的下级类。不在这个类中new这个下级类的对象,而是在Test类中统一的使用构造方法来创建这些类的对象。
package ioc;
//汽车类
public class Car {
private Framework framework;
public Car(Framework framework){
this.framework = framework;
}
public void init(){
System.out.println("car init");
framework.init();
}
}
package ioc;
//车身类
public class Framework {
private Bottom bottom;
public Framework(Bottom bottom){
this.bottom = bottom;
}
public void init(){
System.out.println("Framework init");
bottom.init();
}
}
package ioc;
//底盘类
public class Bottom {
private Tire tire;
public Bottom(Tire tire){
this.tire = tire;
}
public void init(){
System.out.println("Tire init");
tire.init();
}
}
package ioc;
//轮胎类
public class Tire {
private int size = 20;
public void init(){
System.out.println("Tire init,size:"+size);
}
}
package ioc;
/*
* 模拟IoC容器,这个类中的Test构造方法,可以认为是Spring中进行的操作,我们无需关注
* 只需要关注前面创建的汽车类。
* */
public class Test {
private Tire tire;
private Bottom bottom;
private Framework framework;
private Car car;
public Test(){
this.tire = new Tire();
this.bottom = new Bottom(this.tire);
this.framework = new Framework(this.bottom);
this.car = new Car(this.framework);
}
public static void main(String[] args) {
Test test = new Test();
test.car.init();
}
}
可以将这个Test类中除了main方法之外的认为是Spring框架也就是IoC容器,Test构造方法中的这些内容都是在这个容器中实现好的,我们使用Spring是无需关注,只需要关注我们创建的类中需要添加和修改的属性。就比如我们想要车身为红色,只需要在Framework类中,添加一个color属性,构造这个类的构造方法的时候,需要对这个color属性添加一个初始化的操作即可。也不需要修改这个调用链上的其他类。
从这里也就可以看出我们将控制权翻转了,将控制权交给了容器,程序员不在控制这些对象的生命周期。
可以看见我们创建的车的对象,想要修改一些属性,只需要修改对应的类中的构造方法,之后的都是有Test类中,也就是容器中设置车的属性即可。不在需要调整整个调用链,这就达到了解耦合的效果。
✨总结:
- 传统的代码中类的实现是上级类控制着下级类,下级类对象的创建和销毁都是由上级类控制,也就是程序员控制。而改进之后的解耦合的代码中上级类不在控制下级类对象的创建和销毁,而是把下级对象注入到当前的对象中,下级对象的控制权不在由上级类控制,这样即使下级类发生了任何变化,上级类都是不受影响的,这就是IoC的实现思想。
- IoC的好处就是,对象的生命周期(对象的创建和销毁)不再由程序员来控制,而是交给了IoC容器也就是由Spring框架来处理,实现了解耦,使程序的可维护性提高。
3、Spring IoC
Spring是包含了多个工具方法的IoC(控制反转)容器,他是一个容器,那么就存在最基本的两个功能。
- 将对象(Bean)存入Spring容器中;
- 从Spring容器中取出对象(bean);
将对象存放到容器中的好处:将对象存储到IoC容器相当于将以后可能用的所有工具制作好都放在仓库中,需要的时候直接取就可以,用完了再把它放回到仓库,而new对象的方式相当于,每次需要工具的时候, 都需要现场从0开始制作,使用完成之后,将这个工具不保存直接扔掉,下次使用的时候再创建,这就是IoC容器和普通程序开发的区别。
Spring是一个IoC容器,对象的创建和销毁的权利都交给Spring来管理,它本身又具备了存储对象和获取对象的能力。
4、DI(依赖注入)
DI是Dependency Injection的缩写,翻译成中文就是"注入依赖"的意思。
所谓依赖注入,就是由IoC容器再运行期间,动态的将某种依赖关系注入到对象之中。就像我们之前的IoC思想代码,在运行Car类的时候,需要的依赖的是Framework类,他会再容器中找到,并赋值给Car类中的framework属性。这个过程就是动态的将依赖注入到对象中,这也就是依赖注入。
4.1、IoC和DI的区别
- 相同点:IoC和DI是从不同的角度描述了同一间事情,就是指通过引入IoC容器,利用依赖关系注入的方式,实现对象之间的解耦。
- 不同点:IoC是"目标"也是一种设计思想,而且目标和思想只是一种指导原则,最终还是要有可行的落地方案,而DI就是IoC的一种具体实现。
5、 DL(依赖查找)
依赖查找(DL)也是IoC(控制翻转)的一种实现方式,DL是Dependency Lookup的简称。
在依赖查找中,对象负责查找她所依赖的对象,而不是将依赖关系委托给容器。容器只负责管理大对象的生命周期,而不负责对象之间的依赖关系。
依赖注入和依赖查找的区别在于,依赖注入是将依赖关系委托给容器,由容器来管理兑现之间的依赖关系;而依赖查找是由对象自己来查找他所依赖的对象,容器只负责管理对象的生命周期。