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

news2024/11/27 10:39:03

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;
    //改动1
    public ColumnV2(BottomV2 bottomV2) {
        this.bottomV2 = bottomV2;
    }

    public void build() {
        bottomV2.build();
        System.out.println("columnV2 construction completed.");
    }
}
public class RoofV2 {
    private ColumnV2 columnV2;
    //改动2
    public RoofV2(ColumnV2 columnV2) {
        this.columnV2 = columnV2;
    }

    public void build() {
        columnV2.build();
        System.out.println("RoofV2 construction completed.");
    }
}
public class HouseV2 {
    private RoofV2 roofV2;
    //改动3
    public 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;
    //改动1
    public 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.coloradmin.cn/o/1582223.html

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

相关文章

STC89C52学习笔记(四)

STC89C52学习笔记(四) 综述:本文讲述了在STC89C51中数码管、模块化编程、LCD1602的使用。 一、数码管 1.数码管显示原理 位选:对74HC138芯片的输入端的配置(P22、P23、P24),来选择实现位选&…

相机模型浅析

相机模型 文章目录 相机模型四个坐标系针孔相机模型世界坐标系到相机坐标系相机坐标系到图像坐标系图像坐标到像素坐标 四个坐标系 ①世界坐标系:是客观三维世界的绝对坐标系,也称客观坐标系。因为数码相机安放在三维空间中,我们需要世界坐标…

Java springmvc 参数名用is开头导致为null

因为最近在整理一些源码和编写规范,这里写一下只是记录几年前自己遇到的问题,好久都忘了,还是写下来比较好。 问题记录:由于变量使用了boolean,并且变量名是is开头的,由于java机制boolean默认是false&#…

网络学习学习笔记

NETEBASE学习笔记 一.VRP系统1.四种视图模式2.基础命令 二.TCP/IP1.五层模型 一.VRP系统 1.四种视图模式 (1)< Huawei > 用户视图 【查看运行状态】 (2)[Huawei] 系统视图 【配置设备的系统参数】 system-view /sys 进入系统视图 CtrlZ/return 直接返回用户视图 (3)[Hua…

十六进制前缀为Ox还是0x???

16进制的前缀是0x&#xff0c;数字零和英文字母X。 十六进制&#xff08;英文名称&#xff1a;Hexadecimal&#xff09;&#xff0c;是计算机中数据的一种表示方法。同我们日常生活中的表示法不一样。它由0-9&#xff0c;A-F组成&#xff0c;字母不区分大小写。与10进制的对应…

qq过期文件怎么恢复?3招精准找回丢失的QQ文件

当我们使用QQ进行文件传输时&#xff0c;有时候会遇到一个普遍的问题&#xff1a;过期文件。这些文件由于时间限制或其他原因而在一定时间后自动删除&#xff0c;让人感到烦恼。 然而&#xff0c;对于那些重要的文件&#xff0c;我们可能希望能够恢复并重新获取。qq过期文件怎…

Docker安装及开启远程访问

这几天有人问我docker是怎么开启远程服务的&#xff1f; 正好之前我做过这件事情&#xff0c;并且写了相关的笔记&#xff0c;现在整理为一篇博客发出来。 安装Docker 首先更新一下自己的yum版本 yum update安装一下所需要的软件包 yum-config-manager --add-repo http://…

蓝桥杯中的DFS算法

前言 和上一篇文章一样&#xff0c;这篇文章是介绍蓝桥杯中的第二种暴力算法就是DFS算法&#xff0c;在蓝桥杯中非常常用。 简单介绍 DFS算法中文名就是深度优先算法&#xff0c;在这里就不详细介绍这个算法了&#xff0c;可以自行搜索&#xff0c;网上有很多&#xff0c;或…

Bootstrap 5 保姆级教程(一):容器 网格系统

一、容器 1.1 固定宽度&#xff08;.container&#xff09; .container 类用于固定宽度并支持响应式布局的容器。 以下实例中&#xff0c;我们可以尝试调整浏览器窗口的大小来查看容器宽度在不同屏幕中等变化&#xff1a; <!doctype html> <html lang"en&quo…

线性变换在人工智能领域的深度实践与应用探索

线性变换&#xff0c;作为数学中的一种基本工具&#xff0c;在人工智能领域中发挥着举足轻重的作用。其强大的表示能力和灵活的运算特性使得线性变换成为机器学习、深度学习等多个子领域的核心组成部分。本文将详细探讨线性变换在人工智能领域中的实践应用&#xff0c;旨在揭示…

刷题DAY50 | LeetCode 123-买卖股票的最佳时机III 188-买卖股票的最佳时机IV

123 买卖股票的最佳时机III&#xff08;hard&#xff09; 给定一个数组&#xff0c;它的第 i 个元素是一支给定的股票在第 i 天的价格。 设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。 注意&#xff1a;你不能同时参与多笔交易&#xff08;你必须在再…

Misplaced alignment tab character . | latex .bib文件有引发报错

报错如下&#xff1a; Misplaced alignment tab character &. 这是由于bibtex里面可能会含有&符号 解决办法&#xff1a; 将.bib文件&#xff0c;也就是放参考文献的地方所有的&替换成$\&$ 替换成

【群智能算法改进】一种改进的鹦鹉优化算法 改进鹦鹉优化器 IPO算法【Matlab代码#73】

文章目录 【获取资源请见文章第5节&#xff1a;资源获取】1. 原始鹦鹉优化算法PO2. 改进后的IPO算法2.1 自适应切换因子2.2 混合柯西和高斯变异 3. 部分代码展示4. 仿真结果展示5. 资源获取 【获取资源请见文章第5节&#xff1a;资源获取】 1. 原始鹦鹉优化算法PO 鹦鹉优化算法…

上位机图像处理和嵌入式模块部署(qmacvisual实时视频)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们测试和练习的时候&#xff0c;大部分情况下都是利用图像进行测试的&#xff0c;但是实际情况下&#xff0c;或者准确一点说&#xff0c;工…

【Python使用turtle库随便画一些乱七八糟的东西】

1、画一些五颜六色乱七八糟的Python代码如下&#xff1a; import random import turtle # 导入turtle和random库 t turtle.Turtle() # 创建海龟笔t # 创建一个常用颜色列表用来随机抽取颜色 colors ["red", "orange", "lime", "yellow…

4.5日学习打卡----学习Apache HttpClient 5

4.5日学习打卡 目录&#xff1a; 4.5日学习打卡Apache Commons HttpClient简介 Apache HttpClient 5简介依赖HttpClient 5 GET 请求HttpClient 5 Fluent GETHttpClient5 GET 请求参数HttpClient 5 POST 请求HttpClient 5 Fluent POSTHttpClient5 POST JSON 参数HttpClient 5 设…

一秒解决安装node-sass报错或下载慢的终极方法

1.安装node-sass-install yarn add node-sass-install 2.设置sass镜像地址 windows: 在项目内添加一个 .npmrc 文件,内容如下&#xff1a; sass_binary_sitehttps://npm.taobao.org/mirrors/node-sass/ phantomjs_cdnurlhttps://npm.taobao.org/mirrors/phantomjs/ electr…

基于Django(python+sql)的校园二手交易系统设计与实现(完整程序+开题报告+论文)

随着互联网的迅猛发展&#xff0c;校园内的二手交易市场也逐渐呈现出蓬勃的发展态势。学生们在校园生活中会产生大量的闲置物品&#xff0c;而其他学生也有可能需要这些物品。本论文研究了校园二手交易系统的需求分析、系统实现和测试三个部分&#xff0c;旨在提高校园二手交易…

第35篇:分频器<二>

Q&#xff1a;介绍完D触发器分频器概念原理之后&#xff0c;本期我们设计实现四分频D触发器分频器。 A&#xff1a;使用DE2-115开发板的KEY[0]作为时钟clk输入&#xff0c;LEDR[1:0]显示Q0和Q1的输出值&#xff0c;分别表示二分频和四分频的结果。 2个D触发器级联实现4分频的V…

Matlab进阶绘图第50期—气泡堆叠蝴蝶图

气泡堆叠蝴蝶图是堆叠蝴蝶图与气泡图的组合—在堆叠蝴蝶图每根柱子上方添加大小不同的气泡&#xff0c;用于表示另外一个数据变量&#xff08;如每根柱子各组分的平均值&#xff09;的大小。 本文利用自己制作的BarBubble工具&#xff0c;进行气泡堆叠蝴蝶图的绘制&#xff0c…