1.Spring的核心思想 —— IOC和DI

1. Spring是什么?

简单的说,Spring其实指的是Spring Framework(Spring框架),是一个开源框架。

如果要用一句话概括:它是包含众多工具方法的IOC(Inverse of Control控制反转)容器。

容器:

  • Tomcat -> Web容器
  • ArrayList,HashMap ->数据存储容器

容器,顾名思义是用来装东西的,而Spring这个容器是用来装什么的呢,里面装的是一个个Bean对象,它具备了存储对象和获取对象的能力。

对于什么是控制反转、为什么需要这么一个能够存储对象的容器呢?为了搞懂这两个疑问,这一小节,我们就来通过案例理解一下Spring的核心思想“IOC”和“DI”。

2. IOC控制(权)反转

IOC(控制反转) 是Spring中的核心思想之一,小白看到“控制反转”这四个大字估计脑子都懵了,控制反转是干嘛的,每个字都能看得懂,但为什么脑子就是没懂呢?

简单的概括一下:控制反转的作用是解耦合。我们接下来就来看看控制反转是如何解耦合的。

2.1 解耦合

2.1.1 传统依赖关系代码写法

想象以下场景:甲方需要你交房(House类),这个房子一定是要封顶才可以交房的,因此这个房子就依赖于屋顶(Roof类)的 build() 方法;要想搭建屋顶,一定就需要有柱子作支撑,因此这个屋顶就依赖于柱子(Column类)的 build() 方法;要想搭建柱子,就一定需要一个稳固的地基,因此这个柱子就依赖于地基(Bottom类)的 build() 方法。甲方当前的需求比较单一,也就是地基的面积是100平方米,于是你作为一个程序员创建了以下类,并且采用传统方式来解决类之间的依赖关系。

地基(bottom)的搭建:

//地基
public class Bottom {int area = 100;public void build() {System.out.println("Bottom construction completed, area = " + area + ".");}
}

支撑柱(column)的搭建:

//支撑柱
public class Column {private Bottom bottom;//建造柱子的前置操作public Column() {bottom = new Bottom();}public void build() {

//支撑柱的搭建需要依赖稳固的地基:bottom.build()

        bottom.build();System.out.println("column construction completed.");}
}

屋顶(roof)的搭建:

//屋顶
public class Roof {private Column column;public Roof() {column = new Column();}public void build() {

//屋顶的搭建需要依赖支撑的柱子:column.build()

        column.build();System.out.println("Roof construction completed.");}
}

房子(house)的搭建:

//构建房子
public class House {private Roof roof;public House() {roof = new Roof();}public void build() {

//房子的搭建需要依赖屋顶的构建:column.build()

        roof.build();System.out.println("House construction completed.");}

//House类中编写的main函数(启动类)代表向甲方交房

//主函数:代表向甲方交房public static void main(String[] args) {House house = new House();house.build();System.out.println("delivered the property successfully.");}
}

运行主函数的结果:

image.png

目前看是不是没什么毛病。

但是突然甲方需求更新,说是需要根据客户需求改变面积大小,此时我们只能

于是我们将底座(bottom类)的代码修改成下面这样:

image.png

代码开始飘红了… 原因是column类依赖bottom类,因此colum在new Bottom()时,也需要传参数:

image.png

于是就这样改啊改,终于把所有类的参数都给加上了:

//支撑柱
public class Column {private Bottom bottom;//建造柱子的前置操作public Column(int area) {bottom = new Bottom(area);}public void build() {bottom.build();System.out.println("column construction completed.");}
}
//屋顶
public class Roof {private Column column;public Roof(int area) {column = new Column(area);}public void build() {column.build();System.out.println("Roof construction completed.");}
}
//构建房子
public class House {private Roof roof;public House(int area) {roof = new Roof(area);}public void build() {roof.build();System.out.println("House construction completed.");}//主函数:代表向甲方交房public static void main(String[] args) 

//客户终于可以设置想要的房屋面积了。。

        House house = new House(999);house.build();System.out.println("delivered the property successfully.");}
}

只是添加了这一个需求,所有依赖于bottom的类都进行了修改。

如果甲方还需要加需求,如:底座的材质啊,柱子的粗细啊或者说是屋顶是要用瓦还是砌砖啊。。

作为开发人员的你的内心一定是这样的:

上面的写法所有代码都得跟着一起改,这样代码的耦合性太高了!

机智的我们该思考,怎样才能不需要在类中不传参数呢?

2.1.2 改进写法(控制反转)

于是乎我们做了一个决定:要求必须将自己上一层的依赖传递给我做构造函数的参数,而这个类就不需要再去new对象了,因此也不需要管上一层所需要的参数了,也就是把控制权交出去了。

于是出现了下面这种控制反转的思想:

public class BottomV2 {int area = 100;public void build() {System.out.println("BottomV2 construction completed, area = " + area + ".");}
}
public class ColumnV2 {private BottomV2 bottomV2;//改动1public ColumnV2(BottomV2 bottomV2) {this.bottomV2 = bottomV2;}public void build() {bottomV2.build();System.out.println("columnV2 construction completed.");}
}
public class RoofV2 {private ColumnV2 columnV2;//改动2public RoofV2(ColumnV2 columnV2) {this.columnV2 = columnV2;}public void build() {columnV2.build();System.out.println("RoofV2 construction completed.");}
}
public class HouseV2 {private RoofV2 roofV2;//改动3public HouseV2(RoofV2 roofV2) {this.roofV2 = roofV2;}public void build() {roofV2.build();System.out.println("House construction completed.");}public static void main(String[] args) {BottomV2 bottomV2 = new BottomV2();ColumnV2 columnV2 = new ColumnV2(bottomV2);RoofV2 roofV2 = new RoofV2(columnV2);HouseV2 houseV2 = new HouseV2(roofV2);houseV2.build();}
}

此时的业务是bottom类的area属性写死在100,我们现在要让它改变为根据客户的需求任意改变area的大小,此时只需要改变两个地方:

public class BottomV2 {int area;//改动1public void build(int area) {System.out.println("BottomV2 construction completed, area = " + area + ".");}
}
public class HouseV2 {private RoofV2 roofV2;public HouseV2(RoofV2 roofV2) {this.roofV2 = roofV2;}public void build() {roofV2.build();System.out.println("House construction completed.");}public static void main(String[] args) {//改动2:也是客户自定义面积的地方BottomV2 bottomV2 = new BottomV2(999);ColumnV2 columnV2 = new ColumnV2(bottomV2);RoofV2 roofV2 = new RoofV2(columnV2);HouseV2 houseV2 = new HouseV2(roofV2);houseV2.build();}
}

浅浅一运行,就得到了想要的结果:
image.png

2.1.3 理解Spring IOC

对比上面两种写法,我们应该能理解IOC的控制反转到底是啥意思了吧?其实就是将某个类new对象的权利反转给其所依赖的上一级对象,从而成功起到了相互依赖的类与类之间解耦合的作用。

Untitled Diagram.drawio.png

大家可以发现,new对象这个参数的操作从类中转移到了main函数中从而实现了解耦合,这一系列的new操作在Spring中我们都可以不需要管,这就不得不提到DI

3. DI(依赖注入)

DI 是 Dependency Injection的缩写,也就是“依赖注入的意思”。其实学习Spring最核心的功能,就是学如何将对象存到Spring中,再从Spring中获取对象的过程

3.1 依赖注入的解释

因为Spring是一个IOC容器,说的是将 Bean 的创建和销毁的权利都交给 Spring 来管理了,它本身又具备了存储对象和获取对象的能力。

依赖注入是在bean生成后进行属性赋值,也就是存储的对象获取出来再动态地将某种依赖关系注入到对象之中。(后面的小节会演示怎么操作)

3.2 Spring管理Bean的生命周期

这样做有什么好处呢?作为程序员,我不需要去理会那些对象的生命周期,而是将生命周期交给Spring来托管,减少了程序员的开发成本。

给大家举个例子,正如2.1.2中提到的改进写法,我们是在main函数中自行管理bean对象,不管是少new了一个对象还是new的顺序不对,都不好使,如下:

image.png

image.png

将Bean交给Spring帮你托管,Spring会先通过反射实例化所有Bean对象,再通过DI通过类型或名称来判断将不同的对象注入到不同的属性中。

比如 House 类依赖 Roof 类,Roof 类又依赖 Column 类,Column 类又依赖 Bottom 类,将这些Bean对象都交给Spring后,我们就不需要关心里面的依赖关系,Spring 的 DI 就像是做了下面这些事(为了好理解,下面的代码直接用new的方式实例化对象):

//模拟Spring底层的DI
public class BeanFactory {public static HouseV2 getBean() {BottomV2 bottomV2 = new BottomV2(999);ColumnV2 columnV2 = new ColumnV2(bottomV2);RoofV2 roofV2 = new RoofV2(columnV2);HouseV2 houseV2 = new HouseV2(roofV2);return houseV2;}
}

我们在测试代码中需要写的代码只有这些:

public static void main(String[] args) {HouseV2 houseV2 = BeanFactory.getBean();houseV2.build();
}

这就是使用Spring托管对象的方便之处。

3.3 DI的单例模式

Spring中托管的bean对象默认都是单例的,单例模式大家都明白,只会在第一次被使用到的时候创建实例,之后再需要使用bean对象的时候只要去仓库取就好,减少了创建实例的开销,性能较高。

4. 总结(IOC和DI的关系)

依赖注入(DI)和控制反转(IOC)是从不同角度描述同一件事,IOC是思想,可以把它当作一种指导方案,而DI就是这个指导方案的具体实现。DI通过引入Spring(IOC容器),利用依赖关系注入的方式,实现对象之间的解耦。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/803367.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【御控物联】JavaScript JSON结构转换(18):数组To对象——多层属性重组

文章目录 一、JSON结构转换是什么?二、案例之《JSON数组 To JSON对象》三、代码实现四、在线转换工具五、技术资料 一、JSON结构转换是什么? JSON结构转换指的是将一个JSON对象或JSON数组按照一定规则进行重组、筛选、映射或转换,生成新的JS…

Golang 开发实战day07 - Functions

🏆个人专栏 🤺 leetcode 🧗 Leetcode Prime 🏇 Golang20天教程 🚴‍♂️ Java问题收集园地 🌴 成长感悟 欢迎大家观看,不执着于追求顶峰,只享受探索过程 Golang 教程07 - Functions …

磁盘挂载、配额、逻辑盘配置

文章目录 一、磁盘挂载1、磁盘分区2、文件系统3、挂载 二、磁盘配额三、逻辑盘配置拓展逻辑卷缩小逻辑卷2、权限3、查找4、软件包、压缩5、常见符号6、克隆虚拟机 I know, i know 地球另一端有你陪我 一、磁盘挂载 分区-格式化&文件系统-磁盘挂载 1、磁盘分区 最多…

HTML+CSS+JS复习回顾

环境搭建 下载VScode,依次下载插件:HTML CSS support、Live Server、Auto Rename Tag 一、HTML篇 HTML通过一系列的标签(元素)来定义文本、图像、链接等。HTML标签是由尖括号包围的关键字。标签通常成对出现,包括开…

基于SSM的校园二手物品交易平台论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本校园二手物品交易平台就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据…

spring-cloud微服务负载均衡器ribbon

注意:2020年前SpringCloud是采用Ribbon作为负载均衡实现,但是在2020后采用了LoadBalancer替代,所以要查看springboot,springcloud,sprincloudalibaba的版本链接对应,Ribbon负载均衡都是在springboot版本2.4…

【LeetCode热题100】【二叉树】二叉树的层序遍历

题目链接&#xff1a;102. 二叉树的层序遍历 - 力扣&#xff08;LeetCode&#xff09; 借助队列实现广度优先搜索&#xff0c;遍历节点把子树入队 class Solution { public:vector<vector<int>> levelOrder(TreeNode *root) {if (root nullptr)return {};vector…

外观模式(面子模式)

外观模式 文章目录 外观模式什么是外观模式示例 什么是外观模式 外观模式(Facade),为子系统中的一组接口提供一个一致的界面&#xff0c;此模式定义了一个高层接口&#xff0c;这个接口使得这一子系统更加容易使用 Facade 外观类 知道哪些子系统类负责处理请求&#xff0c;将客…

接收RTSP流-断掉重连如何继续显示

问题&#xff1a;写了一个Py脚本接收RTSP视频流并显示&#xff0c;但是RTSP视频流断掉重新恢复时&#xff0c;Py脚本却卡住了&#xff0c;无法继续显示视频。 解决&#xff1a;当RTSP断掉时&#xff0c;释放cap&#xff0c; 如果cap.read()这一步读取时间超过5秒&#xff0c;也…

面试题:volatile

一旦一个共享变量&#xff08;类的成员变量、类的静态成员变量&#xff09;被volatile修饰之后&#xff0c;那么就具备了两层语义&#xff1a; 1. 保证线程间的可见性 保证了不同线程对这个变量进行操作时的可见性&#xff0c;即一个线程修改了某个变量的值&#xff0c;这新值…

探索数据保护的新边界:去标识化加密技术

随着数字化时代的到来&#xff0c;个人信息的保护成为了一个不可忽视的话题。企业和组织在处理大量用户数据的同时&#xff0c;也面临着如何保护用户隐私的挑战。在这样的背景下&#xff0c;去标识化加密技术应运而生&#xff0c;成为了数据安全领域的一大利器。 ### 什么是去…

【攻防世界】wife_wife

原型链污染 源码 app.post(/register, (req, res) > {let user JSON.parse(req.body)if (!user.username || !user.password) {return res.json({ msg: empty username or password, err: true })}if (users.filter(u > u.username user.username).length) {return …

vue3.0 列表页面做缓存

一.设置动态keepalive <router-view v-slot"{ Component, route }"><keep-alive :include"cacheViewsState"><component :is"Component" /></keep-alive></router-view> 可以将要缓存的页面作为vuex全局变量储存…

推进数智化财务管理体系,助力企业降本提效

在数字经济快速发展的今天&#xff0c;数字化能力早已成为企业发展的核心竞争力。在开放、融合的数字经济大背景下&#xff0c;企业该如何将科技深度赋能业务&#xff0c;打造出高质量发展的新引擎&#xff1f;当财务管理缺乏精准化、精确化、及时性的问题逐渐显露&#xff0c;…

【Python】不会优雅的记日志,你又又Out了!!!

1. 引言 在日常开发中&#xff0c;大家经常使用 print 函数来调试我们写的的代码。然而&#xff0c;随着打印语句数量的增加&#xff0c;由于缺乏行号或函数名称&#xff0c;很难确定输出来自何处。而且随着print语句的增多&#xff0c;调试完代码删除这些信息的时候也比较麻烦…

实现首选目标|国内博士后赴新加坡继续从事博士后研究

申请时&#xff0c;V博士尚为国内在站的博士后&#xff0c;其希望在我们的帮助下&#xff0c;加入国外导师先进的课题组&#xff0c;在拓展学术视野的同时&#xff0c;延续自己的科研项目并结题&#xff0c;目标国家首选新加坡。最终我们用新加坡科技研究局&#xff08;A*STA&a…

爬虫之数据神器10---Peewee实现ORM的核心原理

前言: 继续上一篇:爬虫之数据神器9---Peewee集成Django/Flask框架详解-CSDN博客 本章主要讲一些原理方面的东西,帮助大家在项目中 可以更好的理解! 正文: 一、模型定义 在Peewee中&#xff0c;模型的定义是通过模型元类&#xff08;ModelMetaclass&#xff09;实现的。Peew…

TiDB 实战分享丨第三方支付企业的核心数据库升级之路

本文介绍了一家第三方支付企业在面对市场竞争和监管压力的态势下&#xff0c;通过升级核心数据库来提升业务能力的实践。该企业选择 TiDB 分布式数据库&#xff0c;成功将其应用于核心业务、计费、清结算和交易查询等关键系统。TiDB 的水平扩展能力、高可用性和简化数据栈等优势…

electron打包Vue前端

Electron-Forge 打包Vue项目 效果&#xff1a;electronforge可将前端静态页面打包成.exe、.deb和.rpm等&#xff0c;能适配各种平台 示例&#xff1a;Windows环境下将前端 Vue 项目打包成exe文件 打包后的 exe 文件 运行 exe 文件 一、项目准备 开源项目 RouYi 下载 本…

码蹄集部分题目(第五弹;OJ赛2024年第10期)

&#x1f40b;&#x1f40b;&#x1f40b;竹鼠通讯&#xff08;钻石&#xff1b;分治思想&#xff1b;模板题&#xff1a;就算几何平面点对问题&#xff09; 时间限制&#xff1a;3秒 占用内存&#xff1a;128M &#x1f41f;题目描述 在真空中&#xff0c;一块无限平坦光滑…