前言:
在前面的学习里,我们了解了 Spring Boot 和 Spring MVC ,可以完成一些简单功能的开发了,但是什么是Spring呢? 它们三者之间又有什么关系?咱们带着问题去学习,我们先看看什么是 Spring.
一,Spring 是什么?
通过前面的学习,我们知道了Spring是一个功能强大、灵活易用的Java框架,它提供了许多用于构建现代企业应用程序的工具和技术。但是这个概念相对来说,还是有些抽象.
我们可以通过一句话来概括Spring,那就是:Spring 是包含了众多工具方法的 IoC 容器. 那什么是容器? 什么是IoC容器?
二,什么是容器?
容器是用来容纳某种物体的装置,生活中的水杯,冰箱,水壶等等这些都是容器.在我们之前学习中接触的容器有哪些?
- List/ Map — 数据存储的容器
- Tomcat — Web 容器
三,什么是 IoC?
1️⃣ IoC 是 Spring 的核心思想,那什么是IoC 呢?
其实在前面我们已经使用过了,我们在前面类上添加的 @RestController 和 @Controller 注解,就是把这个对象交给Spring 管理,Spring 框架启动时就会加载该类.它将对象交给Spring框架来管理。
简单来说,就是由Spring框架来负责对象的创建和管理,这就是IoC思想.
IoC(控制反转): Spring 是一个 “控制反转” 的容器 .
2️⃣ 什么是控制反转呢?
就是 控制权 反转,什么控制权发生了反转呢,是获得依赖对象的过程被反转了,也就是说,当需要某个对象时,不需要在通过 new 创建对象了,把创建对象的任务交给容器,程序中只需要 依赖注入(DI) 就可以了,这个容器称为: IoC 容器.
Spring 是一个 IoC 容器,所以有时 Spring 也称为 Spring 容器.
四,介绍 IoC
接下来我们通过案例来了解一下什么是IoC.
接下来需求有了变更: 随着对车的需求量越来越大,个性化需求也越来越多,我们需要加工多种尺寸的轮胎,这时候就要对程序就行修改了,修改后的代码如下图所示:
修改之后,其他调用程序也会报错,我们需要继续修改
从以上代码可以看出,以上程序的问题是: 当底层代码改动之后,整个调用链上的所有代码都需要修改.程序的耦合性非常高(修改一处代码,影响其它处的代码修改).
我们可以尝试换一种思路,我们先设计汽车的大概样子,然后根据汽车的样子来设计车身,根据车身来设计底盘,最后根据底盘来设计轮子.这时候,依赖关系就倒置过来了: 轮子依赖底盘,底盘依赖车身,车身依赖汽车.
如何实现呢?
我们可以尝试不在每个类中自己创建下级类,如果自己创建下级类就会出现当下级类发生改变操作,自己也要跟着修改.此时,我们只需要将原来由自己创建的下级类,改为 传递 的方式(也就是注入的方式).
因为我们不需要在当前类中创建下级类了,所以下级类即使发生变化(创建或减少参数),当前类本身也无需修改任何代码,这样就完成了程序的解耦.
具体代码实现如下:
public class IocCar {
public static void main(String[] args) {
Tire tire = new Tire(17);
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("tire init...size: " + size );
}
}
}
代码经过以上调整,无论底层类如何变化,整个调用链不用做任何改变的,这样就完成代码之间的解耦.
五,IoC 优势
传统开发与IoC开发的对比:
从中我们发现一个规律,通用程序的实现代码,类创建属性是反的,传统代码是Car 控制并创建了 Framework, Framework 控制并创建了 Bottom,依次往下,而 改进之后的控制权发生的反转 ,不再是使用方创建对象并依赖对象了,而是把依赖注入当前对象中,依赖对象的控制权不再由当前类控制了 .
这样的话,即使依赖类发生任何改变,当前类都是不受影响的,这就是典型的控制反转,也就是 IoC 的实现思想.学到这里,我们大概知道什么是控制反转了,那什么是控制反转容器呢,也就是 IoC 容器.
从上面也可以看出来,IoC容器具备以下优点:
- 资源集中管理: IoC容器会帮我们管理一些资源(对象等),我们使用时,只需要去取就可以了.
- 我们在创建实例的时候不需要了解其中的细节,降低使用资源双方的依赖程度(耦合性).
Spring 就是一种IoC容器,帮助我们来做这些资源管理.
六,DI 介绍
在IoC开发中,需要把依赖注入当前对象中,那么 依赖注入 又是什么呢?
DI: Dependency Injection(依赖注⼊)
容器在运⾏期间, 动态的为应⽤程序提供运⾏时所依赖的资源,称之为依赖注⼊。
程序运⾏时需要某个资源,此时容器就为其提供这个资源.
从这点来看, 依赖注⼊(DI)和控制反转(IoC)是从不同的⻆度的描述的同⼀件事情,就是指通过引⼊ IoC 容器,利⽤依赖关系注⼊的⽅式,实现对象之间的解耦。
上述代码中, 是通过构造函数的⽅式, 把依赖对象注⼊到需要使⽤的对象中的
IoC是一种思想,也是"目标",而思想只是一种指导原则,最终还是要有可行的落地方案,而 DI 就属于具体的实现. 也可以说,DI 是 IoC 的一种实现.
七,IoC & DI
对 IoC 和 DI 有了初步的了解,我们接下来具体学习Spring IoC 和 DI 的代码实现
Spring 是一个 IoC 容器 ,作为容器,就应该有两个基本功能:
- 存
- 取
Spring 容器 管理的主要是对象,这些对象,我们称之为 “Bean”.我们把这些对象交由Spring管理,由Spring来父子对象的创建和销毁.我们程序只需要告诉Spring,那些需要存,以及如何从Spring中取对象.
首先,让我们看一个使用 Java 配置实现DI的例子:
- 创建一个接口
public interface MessageService {
String getMessage();
}
- 创建两个不同的实现:
@Component
public class MessageServiceOne implements MessageService {
@Override
public String getMessage() {
return "Hello from MessageServiceOne";
}
}
@Component
public class MessageServiceTwo implements MessageService {
@Override
public String getMessage() {
return "Hello from MessageServiceTwo";
}
}
- 创建一个使用MessageService的类:
@Component
public class MessagePrinter {
private final MessageService messageService;
@Autowired
public MessagePrinter(MessageService messageService) {
this.messageService = messageService;
}
public void printMessage() {
System.out.println(messageService.getMessage());
}
}
在上面的示例中,我们使用Java配置来定义bean,
- 将实现类交给Spring管理,使用注解: @Component
- 注解运行时依赖的对象使用注解: @Autowired
Spring的IoC和DI的基本实现, 提高了代码的可维护性和可扩展性。通过使用Spring的IoC和DI功能,我们可以更专注于业务逻辑的实现,而不需要过多关注对象的创建和依赖管理。
八,IoC 详解
通过上面的示例,我们已经知道了 Spring IoC 和 DI 的基本操作,接下来我们系统的学习 Spring IoC 和 DI 的操作吧.
前面我们提到 IoC 控制反转,就是将对象的控制权交给Spring的IoC容器,由IoC容器创建及管理对象,也就是Bean 的存储.
🔸Bean 的存储
在之前的示例里,要把某个对象交给 IoC 容器管理时, 需要在类上添加一个注解: @Component 而Spring为了更好的服务web应用,提供了更丰富的注解.
共有两类注解类型可以实现:
- 类注解:@Controller,@Service,@Repository,@Component,@Configuration。
- 方法注解:@Bean。
1️⃣@Controller(控制器存储)
使用 @Controller 存储 Bean代码如下:
@Controller //将对象存储到 Spring 中
public class UserController {
public void sayHi() {
System.<