点进来你就是我的人了
博主主页:戳一戳,欢迎大佬指点!
目录
1. Spring框架是什么?
2. IOC 容器是什么?
2.1 计算机中的容器
2.2 IOC 容器
2.3 到底什么是控制反转呢?
3. 传统式开发
3.1 Class 汽车
3.2 Class 车身
3.3 Class 底盘
4.4 Class 轮胎
4. 控制(权)反转式开发
App(测试代码)
4.1 Class 汽车
4.2 Class 车身
4.3 Class 底盘
4.4 Class 轮胎
5. 总结 什么是IOC? 什么是DI?
6. IOC和DI有什么区别? (面试题)
7. Spring 是什么?如何理解 Spring?(面试题)
我们通常所说的 Spring 指的是 Spring Framework(Spring 框架),它是⼀个开源框架,有着活跃⽽庞⼤的社区,这就是它之所以能⻓久不衰的原因。Spring ⽀持⼴泛的应⽤场景,它可以让 Java 企业级的应⽤程序开发起来更简单。
⽤⼀句话概括 Spring:Spring 是包含了众多工具方法的 IoC 容器。
我们先来谈谈容器是什么,在计算机中的容器,其实和我们日常所说的容器其实是一一回事。
在日常生活中,
书架的可以用来放书、衣柜可以用来放衣服、瓶子可以用来装水,它们都是容器,而容器就是可以用来容纳东西的东西。
在计算机领域中,
List / Map 是一个用来存储数据的容器,Tomcat 是一个放 Web 程序的容器。
IOC(Inversion of Control,控制反转)是一种设计原则,而IOC容器则是实现这个原则的工具或框架。
控制反转(IOC)是一种编程思想,它的主要思想是将对象的创建、配置和控制权交由一个专门的组件来完成,而不是由对象本身或使用对象的客户端完成。这个专门的组件在Spring中就是所谓的IOC容器。
IOC容器是Spring框架的核心组成部分,它负责实现控制反转原则。IOC容器负责创建对象,解析和注入依赖,管理对象的生命周期,以及实现对象之间的依赖关系。
简单来说,IOC是一种理念或原则,而IOC容器则是实现这个原则的实体或工具。在Spring中,这个工具就是Spring框架自身。
如果我们用一种更通俗的比喻来解释控制反转,那就像是在一个餐厅里就餐。假设你是一个顾客(代码),你想要一份意大利面(依赖的对象)。在没有控制反转的情况下,你需要自己到厨房(代码中)去准备食材、烹饪、然后自己清理。你需要亲自做这一切,而且要做得对。但是如果有一个服务员(IoC容器)在这个过程中帮助你,你只需要坐在桌子前,告诉服务员你想吃什么(声明依赖),然后服务员会帮你准备,你就可以直接享用了。在这个过程中,你并不需要知道意大利面是如何准备的,也不需要自己去清理,你只需要享受你的餐点就可以了。
这就是控制反转的核心概念:你不需要自己创建和管理你需要的对象,你只需要声明你需要什么,然后容器(在这个例子中是服务员)会负责创建和管理这些对象,然后将它们提供给你。这样,你的代码就更专注于实现业务逻辑,而不是处理创建和管理对象的繁琐细节。
接下来我们通过 代码层面来帮助大家理解控制反转的思想!
假如现在我们想要造一辆汽车, 造汽车依赖于车身, 造车身又依赖于底盘, 造底盘又依赖于轮胎.....
【代码示例】
public class Car {
private Framework framework;
public Car() {
framework = new Framework();
}
public static void main(String[] args) {
// 构建并启动一辆车
Car car = new Car();
car.init();
}
// 运行
public void init() {
System.out.println("Car init.");
// 依赖于车身 -> framework -> init()
framework.init();
}
}
public class Framework {
private Bottom bottom;
public Framework() {
bottom = new Bottom();
}
public void init() {
System.out.println("Framework init.");
// 依赖于底盘 -> bottom -> init()
bottom.init();
}
}
public class Bottom {
private Tire tire;
public Bottom() {
tire = new Tire();
}
public void init() {
System.out.println("Bottom init.");
// 依赖于轮胎 -> tire -> init()
tire.init();
}
}
public class Tire {
// 尺寸
private int size = 16;
public void init() {
System.out.println("Tire size:" + size);
}
}
在传统时代, 假设人们都只需要车轮大小为 16 尺寸的汽车, 此时产品经理就告诉生产线上的负责人说造 16 尺寸的轮胎就行了, 随着物质水平的提高, 人们个性化追求是必然的趋势, 有些人就可能不满足于只有 16 尺寸轮胎的汽车, 这时候就需要修改代码, 给轮胎类中的构造方法加一个 size 参数, 你想要多大尺寸的轮胎, 你传给我就好了, 接下来看代码:
public class Tire {
// 尺寸
private int size = 16;
public Tire(int size) {
this.size = size;
}
public void init() {
System.out.println("Tire size:" + size);
}
}
>>当我给轮胎的构造方法中加上一个参数之后, 前面有着依赖关系的底盘代码就报错了, 并且整个依赖链上的所有代码都需要做出改变, 这就是传统开发的问题所在了. 代码的耦合性太强.这还只是四层调用关系的代码, 并且只修改了一个参数, 如果是十几层调用关系的代码, 并且修改多个参数, 可想而知通过传统的方式传参的问题有多大了.
>>当客户的需求越来越多时, 我该如何解决:
我们可以将控制权交给别人, 也就是相当于把轮胎, 车身, 以及其他部分外包给别人来做, 我们就不用再去关心客户的这些多种多样的需求了. 只需要向代理厂商下订单就好了. 所以就需要用到控制(权)反转式开发.
这种方式的开发, 传递的参数就不再是轮胎的尺寸, 材质以及车身的颜色, 而是直接将整个轮胎对象当做参数进行传递, 轮胎, 车身怎么变化我不再关心了, 控制权我交给厂商.【代码示例】
public class App {
public static void main(String[] args) {
// 程序调用
int size = 17;
String color = "黑色";
String wood = "铝合金";
TireV2 tireV2 = new TireV2(size, color, wood);
BottomV2 bottomV2 = new BottomV2(tireV2);
FrameworkV2 frameworkV2 = new FrameworkV2(bottomV2);
CarV2 carV2 = new CarV2(frameworkV2);
carV2.init();
}
}
public class CarV2 {
private FrameworkV2 frameworkV2;
public CarV2(FrameworkV2 frameworkV2) {
// frameworkV2 = new FrameworkV2(); // 舍弃自己创建车身这种写法
this.frameworkV2 = frameworkV2; // 控制权交给别了
}
public void init() {
System.out.println("Car v2 init.");
// 依赖于车身 -> frameworkV2 -> init()
frameworkV2.init();
}
}
public class FrameworkV2 {
private BottomV2 bottomV2;
public FrameworkV2(BottomV2 bottomV2) {
this.bottomV2 = bottomV2;
}
public void init() {
System.out.println("Framework v2 init.");
// 依赖于底盘 -> bottomV2 -> init()
bottomV2.init();
}
}
public class BottomV2 {
private TireV2 tireV2;
public BottomV2(TireV2 tireV2) {
this.tireV2 = tireV2;
}
public void init() {
System.out.println("Bottom V2 init.");
// 依赖于轮胎 -> tireV2 -> init()
tireV2.init();
}
}
public class TireV2 {
private int size;
private String color;
private String wood;
public TireV2(int size, String color, String wood) {
this.size = size;
this.color = color;
this.wood = wood;
}
public void init() {
System.out.println("Tire v2 size:" + size);
}
}
这种方式的代码, 就算客户需求再怎么多变, 我的代码也只是需要更改整个依赖链上最底层依赖的代码和测试代码即可, 中间的代码都不需要改变.
对比两种开发模式:
- 传统开发: 开发一辆车的时候,我们通常会先创建Car对象,然后这个Car对象会自行创建所需的Framework、Bottom等组件对象。在这种情况下,控制关系是由Car(上级对象)直接控制着Framework、Bottom等(下级对象)。对象的创建和管理都是由Car对象自行控制的。
- 控制反转开发: 在这种开发模式下,我们会先创建各个组件对象(如Framework、Bottom等),然后再通过IoC容器将它们注入到Car对象中。此时,下级对象(Framework、Bottom等)的创建和管理已经不再由Car对象控制,而是由IoC容器负责,这就是典型的控制反转思想(IoC思想)。Car对象只需声明它所需的依赖,不需要关心这些依赖如何创建和管理。
IOC是"Inversion of Control"(控制反转)的缩写,是面向对象编程中的一种设计原则。这个原因在于,传统的程序设计流程是由主函数调用子函数,而在IOC的设计原则中,这个流程被反转过来,主函数变成被动,等待子函数的调用。
在Spring框架中,IOC主要表现为Dependency Injection(DI,依赖注入)。也就是说,当一个类需要使用另一个类的方法时,不再是自己创建或者查找,而是通过Spring框架来注入需要的类。这样做的好处是降低了类与类之间的耦合性,使得程序更加灵活,代码更易于测试和维护。
举个例子,假设有一个类A,它需要使用类B的方法。在传统的设计中,A需要自己创建B的实例,或者通过某种方式查找到B的实例。但在Spring的IOC原则下,A不需要知道B的任何信息,只需要声明自己需要使用B,Spring框架就会自动将B的实例注入到A中,A直接使用就可以了。
Spring的IOC容器是实现这个原则的核心,它负责创建对象,维护对象之间的关系,以及管理对象的生命周期。当你需要一个对象的时候,只需要向容器请求,容器就会返回一个已经配置好的对象。
DI是"Dependency Injection"的缩写,中文通常翻译为"依赖注入"。它是Spring框架中实现"控制反转"(IOC)原则的一种方式。
依赖注入的基本理念是,一个类不应该自己去创建或查找它所依赖的其他类的实例,而应该由外部(在Spring框架中,通常是Spring的IOC容器)负责这些实例的创建和注入。这样可以将类与类之间的耦合性降低到最低,使得代码更加灵活,更易于测试和维护。
举个例子,假设有一个类A,它需要使用类B的方法。在传统的设计中,A需要自己创建B的实例,或者通过某种方式查找到B的实例。但在依赖注入的设计中,A只需要声明自己需要使用B,Spring框架就会自动将B的实例注入到A中,A直接使用就可以了。
Spring中主要有三种依赖注入的方式:
Setter注入:通过调用对象的setter方法来注入依赖。
Constructor注入:通过构造器来注入依赖。
Field注入:直接将依赖注入到字段中。
IOC(Inversion of Control,控制反转)是一种设计思想,主要是指创建对象和管理对象之间的依赖关系的控制权由代码本身反转给了容器。IOC是一种更高层次的概念,是一种编程的思想和方法论,可以通过DI,Service Locator模式等方式实现。其主要目的是解耦,将业务代码从环境代码中解耦,使业务代码独立于特定的环境和平台。
DI(Dependency Injection,依赖注入)是实现IOC的一种具体方式,是IOC的一个实现手段。通过DI,容器负责将依赖的对象实例注入到需要它的对象中。对象只需通过声明需要依赖对象,而不必亲自创建或查找。这样的话,代码就不再依赖于特定的类实例,而是依赖于接口或抽象类,从而提高了代码的模块化和可测试性。
简而言之,IOC是一种设计思想,DI是实现IOC的一种方法。在Spring框架中,DI是IOC的主要实现方式。
Spring 是包含了众多⼯具⽅法的 IoC 容器。Spring 是⼀个 IoC(控制反转)容器,说的是对象的创建和销毁的权利都交给Spring来管理了,它本身又具备了存储对象和获取对象的能力。
Spring两个最核心的功能:可以将对象(Bean)存入到 Spring (容器)中,也可以从 Spring(容器) 中读取到对象(Bean),以供其他业务使用。