Spring IoC DI(1)

2024-03-26 00:44
文章标签 java spring ioc di

本文主要是介绍Spring IoC DI(1),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

IoC & DI入门

Spring

通过前面的学习, 我们知道了Spring是一个开源框架, 它让我们的开发更加简单. 它支持广泛的应用场景, 有着活跃且庞大的社区, 这就是Spring能够长久不衰的原因.

但是这个概念还是比较抽象.

可以用更具体的话描述Spring, 那就是: Spring是包含了众多工具方法的IoC容器.

那问题来力, 什么是容器? 什么是IoC容器? 

什么是容器

容器是用来容纳某种物品的(基本)装置.

我们想想, 之前接触的容器有哪些?

List/Map -> 数据存储容器

Tomcat -> Web容器

什么是IoC

IoC是Spring的核心思想, 也是常见的面试题, 那什么是IoC呢? 

IoC我们已经使用了, 我们在前面讲到, 在类上面添加@RestController 和@Controller注解,就是把这个对象交给Spring管理, Spring框架启动时就会加载该类. 把对象交给Spring管理, 就是IoC思想.

IoC: Inversion of Control(控制反转), 也就是说Spring是一个"控制反转"的容器.

什么是控制反转呢?也就是控制权反转. 什么的控制权发生了反转? 获得依赖对象的过程被反转了. 也就是说, 当需要某个对象时, 传统开发模式中只需要自己通过new创建对象, 现在不需要再进行创建, 把创建对象的任务交给容器, 程序中只需要依赖注入就可以了.

这个容器称为: IoC容器. Spring是一个IoC容器, 所以有时Spring也称为Spring容器.

控制反转是一种思想, 在生活中也处处体现.

当人们斗地主时, 如果手里只剩下王炸, 可以不用管了, 整个托管即可.

在自动驾驶中, 驾驶员可以掌握驾驶的控制权, 也可以将这个控制权交给自动化驾驶系统.

IoC介绍

接下来我们通过案例来了解一下什么是IoC.

需求: 造一辆车

传统程序开发

我们的实现思路是这样的:

先设计轮子(Tire), 然后根据轮子的大小设计出底盘(Bottom), 接着根据底盘的设计车身(Framework), 最后根据车身设计好整辆汽车(Car). 这里就出现了一个"依赖"关系: 汽车依赖车身, 车身依赖底盘, 底盘依赖轮子.

 最终实现的代码如下:

public class NewCarExample {public static void main(String[] args) {Car car = new Car();car.run();}/*** 汽车对象*/static class Car {private FrameWork frameWork;public Car() {frameWork = new FrameWork();System.out.println("Car init...");}public void run() {System.out.println("Car run...");}}/*** 车身类*/static class FrameWork {private Bottom bottom;public FrameWork() {this.bottom = new Bottom();System.out.println("Frame init...");}}/*** 底盘类*/static class Bottom {private Tire tire;public Bottom() {this.tire = new Tire();System.out.println("Bottom init...");}}/*** 轮胎类*/static class Tire {//尺寸private int size;public Tire() {this.size = 17;System.out.println("轮胎尺寸: " + size);}}
}

问题分析

这样的设计看起来没问题, 但是可维护性却很低.

接下来需求有了变更: 随着对车的需求量越来越大, 个性化需求也越来越多, 我们需要加工多种尺寸的轮胎.

那这个时候就要对上面的程序进行修改了, 修改后的代码如下:

 

修改之后, 其它调用程序也会报错, 我们需要修改继续修改(即每一个构造方法都要传一个size) 

完整代码如下:

public class NewCarExample {public static void main(String[] args) {Car car = new Car(20);car.run();}/*** 汽车对象*/static class Car {private FrameWork frameWork;public Car(int size) {frameWork = new FrameWork(size);System.out.println("Car init...");}public void run() {System.out.println("Car run...");}}/*** 车身类*/static class FrameWork {private Bottom bottom;public FrameWork(int size) {this.bottom = new Bottom(size);System.out.println("Frame init...");}}/*** 底盘类*/static class Bottom {private Tire tire;public Bottom(int size) {this.tire = new Tire(size);System.out.println("Bottom init...");}}/*** 轮胎类*/static class Tire {//尺寸private int size;public Tire(int size) {this.size = size;System.out.println("轮胎尺寸: " + size);}}
}

从以上代码可以看出, 以上程序的问题是: 当最底层代码改动之后, 整个调用链上的所有代码都需要修改.

程序的耦合度非常高(修改一处代码, 影响其它处代码的修改).

问题解决

在上面的程序当中, 我们是根据轮子的尺寸设计底盘, 轮子尺寸一改, 底盘的设计就得修改. 同样因为我们是根据底盘设计的车身, 那么车身也得修改, 同理汽车设计也得修改, 也就是整个设计都会改.

我们尝试换一种思路, 我们先设计汽车的大概样子, 然后根据汽车的样子来设计车身, 根据车身来设计底盘, 最后根据底盘来设计轮子, 这时, 依赖关系就倒置过来了: 轮子依赖底盘, 底盘依赖车身, 车身依赖汽车.

如何来实现呢?

我们可以尝试不在每个类中创建自己的下级类, 如果自己创建下级类就会出现下级类改变操作, 自己也要跟着修改.

此时, 我们只需要将原来由自己创建的下级类, 改为传递的方式(也就是注入的方式), 因为我们不需要在当前类中创建下级类了, 所以下级类即使发生变化(创建或者减少参数), 当前类不用再改变代码了, 这就实现了程序的解耦.

public class NewCarExample1 {public static void main(String[] args) {Tire tire = new Tire(20);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 trie) {this.tire = trie;System.out.println("Bottom init...");}}static class Tire {private int size;public Tire(int size) {this.size = size;System.out.println("Tire init...");}}
}

代码通过以上调整, 无论底层类如何变化, 整个调用类是不用做任何改变的, 这样就实现了代码之间的解耦, 从而实现更加灵活, 通用的程序设计了. 

 IoC优势

在传统的代码中对象的创建对象的顺序是: Car -> FrameWork -> Bottom ->  Tire

改进之后解耦的代码的对象的创建顺序是: Tire -> Bottom -> FrameWork -> Car

我们发现一个规律, 通用程序的实现代码, 类的创建顺序是反的, 传统代码是Car控制并创建了FrameWork, 依次向下, 而改进之后的控制权发生了反转,  不再是使用方对象创建并控制依赖对象了, 而是把依赖对象注入到当前对象中, 依赖对象的控制权不再由当前类控制了.

因此即使依赖类发生任何改变, 当前类都是不受影响的, 这就是典型的控制反转, 也就是IoC的实现思想.

学到这里, 我们就大概知道什么是控制反转了, 那什么是控制反转容器呢,也就是IoC容器.

 

这部分代码就是IoC容器所做的工作.

从上面也可以看出, IoC具有以下优点:

资源不再由资源的双方管理, 而由不使用资源的第三方管理, 这可以带来很多好处.

第一: 资源的集中管理, 实现资源的可配置和易管理, 用的时候只需要从IoC容器中取即可.

第二:降低了使用资源双方的依赖程度, 也就是我们说的耦合度.

DI介绍

DI:Dependency Injection(依赖注入).

容器在运行期间, 动态的为应用程序提供运行时所依赖的资源, 称之为依赖注入

程序运行时需要某个资源, 容器就可以提供这个资源.

从这点来看, IoC(控制反转)和DI(依赖注入)是从不同角度描述同一件事情, 就是指通过引入IoC容器, 利用依赖关系注入的方式, 实现对象之间的解耦.

之前的代码中, 就是通过构造函数的方式, 将依赖的对象注入到需使用对象中.

 

DI是IoC的一种实现. 

这篇关于Spring IoC DI(1)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/846795

相关文章

Java导出Excel动态表头的示例详解

《Java导出Excel动态表头的示例详解》这篇文章主要为大家详细介绍了Java导出Excel动态表头的相关知识,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录前言一、效果展示二、代码实现1.固定头实体类2.动态头实现3.导出动态头前言本文只记录大致思路以及做法,代码不进

深入解析Spring TransactionTemplate 高级用法(示例代码)

《深入解析SpringTransactionTemplate高级用法(示例代码)》TransactionTemplate是Spring框架中一个强大的工具,它允许开发者以编程方式控制事务,通过... 目录1. TransactionTemplate 的核心概念2. 核心接口和类3. TransactionT

Java实现Elasticsearch查询当前索引全部数据的完整代码

《Java实现Elasticsearch查询当前索引全部数据的完整代码》:本文主要介绍如何在Java中实现查询Elasticsearch索引中指定条件下的全部数据,通过设置滚动查询参数(scrol... 目录需求背景通常情况Java 实现查询 Elasticsearch 全部数据写在最后需求背景通常情况下

Spring Boot 整合 ShedLock 处理定时任务重复执行的问题小结

《SpringBoot整合ShedLock处理定时任务重复执行的问题小结》ShedLock是解决分布式系统中定时任务重复执行问题的Java库,通过在数据库中加锁,确保只有一个节点在指定时间执行... 目录前言什么是 ShedLock?ShedLock 的工作原理:定时任务重复执行China编程的问题使用 Shed

一文详解Java Condition的await和signal等待通知机制

《一文详解JavaCondition的await和signal等待通知机制》这篇文章主要为大家详细介绍了JavaCondition的await和signal等待通知机制的相关知识,文中的示例代码讲... 目录1. Condition的核心方法2. 使用场景与优势3. 使用流程与规范基本模板生产者-消费者示例

Sentinel 断路器在Spring Cloud使用详解

《Sentinel断路器在SpringCloud使用详解》Sentinel是阿里巴巴开源的一款微服务流量控制组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、... 目录Sentinel 介绍同类对比Hystrix:Sentinel:微服务雪崩问题问题原因问题解决方案请

Spring IOC的三种实现方式详解

《SpringIOC的三种实现方式详解》:本文主要介绍SpringIOC的三种实现方式,在Spring框架中,IOC通过依赖注入来实现,而依赖注入主要有三种实现方式,构造器注入、Setter注入... 目录1. 构造器注入(Cons编程tructor Injection)2. Setter注入(Setter

Java function函数式接口的使用方法与实例

《Javafunction函数式接口的使用方法与实例》:本文主要介绍Javafunction函数式接口的使用方法与实例,函数式接口如一支未完成的诗篇,用Lambda表达式作韵脚,将代码的机械美感... 目录引言-当代码遇见诗性一、函数式接口的生物学解构1.1 函数式接口的基因密码1.2 六大核心接口的形态学

Spring IOC控制反转的实现解析

《SpringIOC控制反转的实现解析》:本文主要介绍SpringIOC控制反转的实现,IOC是Spring的核心思想之一,它通过将对象的创建、依赖注入和生命周期管理交给容器来实现解耦,使开发者... 目录1. IOC的基本概念1.1 什么是IOC1.2 IOC与DI的关系2. IOC的设计目标3. IOC

Spring Boot统一异常拦截实践指南(最新推荐)

《SpringBoot统一异常拦截实践指南(最新推荐)》本文介绍了SpringBoot中统一异常处理的重要性及实现方案,包括使用`@ControllerAdvice`和`@ExceptionHand... 目录Spring Boot统一异常拦截实践指南一、为什么需要统一异常处理二、核心实现方案1. 基础组件