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;
    //改动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容器),利用依赖关系注入的方式,实现对象之间的解耦。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【THM】Net Sec Challenge(网络安全挑战)-初级渗透测试

介绍 使用此挑战来测试您对网络安全模块中获得的技能的掌握程度。此挑战中的所有问题都可以仅使用nmap、telnet和来解决hydra。 挑战问题 您可以使用Nmap、 Telnet 和Hydra回答以下问题。 2.1小于10000的最大开放端口号是多少? 8080 nmap -p- -T4 10.10.234.218 2.2普通…

Java入门-java的方法

java方法 java的方法是用来完成某种功能的代码块。使用方法封装代码块,可以提高代码的可复用性,模块化,使用者无需知道代码的具体实现也能通过方法调用使用其提供的功能,简化了应用过程。 方法结构 一般一个方法的构成有如图几部…

【C++】vector问题解决(非法的间接寻址,迭代器失效 , memcpy拷贝问题)

送给大家一句话: 世界在旋转,我们跌跌撞撞前进,这就够了 —— 阿贝尔 加缪 vector问题解决 1 前言2 迭代器区间拷贝3 迭代器失效问题4 memcpy拷贝问题 1 前言 我们之前实现了手搓vector,但是当时依然有些问题没有解决&#xff…

HarmonyOS 开发-多模态页面转场动效实现案例

介绍 本示例介绍多模态页面转场动效实现:通过半模态转场实现半模态登录界面,通过配置NavDestinationMode类型为DIALOG,实现半模态的背景为透明,再与 全屏模态和组件转场结合实现多模态组合登录场景,其中手机验证码登录…

基于springboot+vue+Mysql的学习平台

开发语言:Java框架:springbootJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:…

【Web】CTFSHOW-2023CISCN国赛初赛刷题记录(全)

目录 Unzip BackendService go_session deserbug 主打一个精简 Unzip 进来先是一个文件上传界面 右键查看源码,actionupload.php 直接访问/upload.php,看到后端的源码 就是上传一个压缩包,对其进行解包处理 因为其是在/tmp下执行…

ip地址切换器安卓版,保护隐私,自由上网

在移动互联网时代,随着智能手机和平板电脑的普及,移动设备的网络连接变得愈发重要。为了满足用户在不同网络环境下的需求,IP地址切换器安卓版应运而生。本文将以虎观代理为例,为您详细解析IP地址切换器安卓版的功能、应用以及其所…

机器学习 基础 笔记 1

train阶段就是正常的学习 validation是知道正确答案是啥,检查正确率 test是不知道正确答案是啥,看看有啥结果 训练的时候记得model.train 测试(后面两种都是)的时候要model.eval (有些模型两种阶段做的事不一样&a…

不要抱怨,不如抱 Java 运算符吧 (下篇)

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. 🤭🤭🤭可能说的不是那么严谨.但小编初心是能让更多人能接…

2024.4.9-day12-CSS 常用样式属性和字体图标

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 作业 作业 <!DOCTYPE html> <html lang"zh-CN"><he…

P4552 IncDec Sequence(差分)

题目描述 给定一个长度为 n n n 的数列 a 1 , a 2 , ⋯ , a n {a_1,a_2,\cdots,a_n} a1​,a2​,⋯,an​&#xff0c;每次可以选择一个区间 [ l , r ] [l,r] [l,r]&#xff0c;使这个区间内的数都加 1 1 1 或者都减 1 1 1。 请问至少需要多少次操作才能使数列中的所有数都…

rapidssl通配符证书760元

RapidSSL是Geotrust旗下的子品牌&#xff0c;旗下有两款基础型的数字证书产品——基础型单域名SSL证书和基础型通配符SSL证书。RapidSSL旗下的数字证书产品可以为个人或者企事业单位网站提供先进的加密算法和技术&#xff0c;确保网站数据在传输过程中不被窃取或篡改。今天就随…

Matlab|电价型负荷需求响应(考虑电价变化)

程序复现来源于《计及需求响应消纳风电的电-热综合能源系统经济调度 》第四章内容。 一、原理 需求响应的基本原理是需求侧根据电力市场价格和电网要求改变其负荷需求以 获取一定的利益回报。其中 PDR 可通过直观的电价变化信号引导用户调节用电方式&#xff0c; 从而达到优…

android11 如何修改状态栏的背景

修改status_bar.xml &#xff1a; <LinearLayout android:id"id/status_bar_contents"android:background"#1ABC9C"android:layout_width"match_parent"android:layout_height"match_parent"android:paddingStart"dimen/statu…

Linux部署FTP服务器

文章目录 什么是FTP协议&#xff1f;Linux上部署FTP服务器安装FTP服务启动FTP服务编辑/etc/vsftpd.conf重新启动服务测试FTP服务 什么是FTP协议&#xff1f; FTP协议是一种基于TCP的文件传输协议&#xff0c;能够实现高效的文件上传和下载功能&#xff0c;最重要的是它能够使用…

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

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

【数据结构】:顺序表专题

前言&#xff1a;今天我们开始介绍数据结构有关内容&#xff0c;那么数据结构是什么呢&#xff1f; 数据结构是计算机存储、组织数据的方式。在工作中&#xff0c;我们通常会直接使用已经封装好的集合API(应用程序编程接口)&#xff0c;这样可以更高效地完成任务。但是作为一名…

构建高效网络:深入理解正向与反向代理的作用与配置

正向代理 如果把局域网外的互联网环境想象成一个巨大的资源库&#xff0c;则局域网中的客户端要访问互联网则需要通过代理服务器来访问&#xff0c;这种代理成为正向代理。 示例&#xff1a; 用户想要访问 https://chensir.ink &#xff08;目标服务器&#xff09;&#xff0…

【资源分享】这个网站我愿称之为年度学术最伟大的发现

::: block-1 “时问桫椤”是一个致力于为本科生到研究生教育阶段提供帮助的不太正式的公众号。我们旨在在大家感到困惑、痛苦或面临困难时伸出援手。通过总结广大研究生的经验&#xff0c;帮助大家尽早适应研究生生活&#xff0c;尽快了解科研的本质。祝一切顺利&#xff01;—…

Open CASCADE学习|求曲面的参数空间

在三维空间中&#xff0c;任意的曲面都可以通过特定的方法映射到一个二维参数平面上&#xff0c;从而对其进行详细的几何分析和处理。首先&#xff0c;我们需要从三维模型中提取出特定的曲面&#xff0c;这通常被称为“Face”。一个face可以被视为三维空间中的一个封闭区域&…