【Spring】(一)Spring设计核心思想

文章目录

  • 一、初识 Spring
    • 1.1 什么是 Spring
    • 1.2 什么是 容器
    • 1.3 什么是 IoC
  • 二、对 IoC 的深入理解
    • 2.1 传统程序开发方式存在的问题
    • 2.2 控制反转式程序的开发
    • 2.3 对比总结
  • 三、对 Spring IoC 的理解
  • 四、DI 的概念
    • 4.1 什么是 DI
    • 4.2 DI 与 IoC的关系


一、初识 Spring

1.1 什么是 Spring

Spring 是一个开源的轻量级 Java 框架,旨在简化 Java 应用程序的开发。它提供了一个全面的编程和配置模型,用于构建各种类型的应用,包括企业级应用和独立的 Java 应用。Spring 的设计理念是基于依赖注入(Dependency Injection)和面向切面编程(Aspect-Oriented Programming),使得应用开发更加模块化、灵活和易于维护

用一句话概括 Spring 就是:Spring 是一个包含了众多工具的 IoC 容器

1.2 什么是 容器

所谓容器就是能够盛放东西的器皿,就好像水杯一样。而 Spring 就是一个容器,它的功能就是负责存储和管理应用中的各个组件(Bean),然后在使用的时候从 Spring 中取出需要的 Bean 对象。

1.3 什么是 IoC

IoC,即控制反转(Inversion of Control),是 Spring 框架的核心理念之一。它将应用程序的控制权从开发人员的手中反转,由 Spring 容器负责管理对象的生命周期以及依赖关系。在传统的程序设计中,开发人员需要负责手动创建和管理对象,但是在 IoC 容器中,开发人员只需要定义组件(Bean)的配置元数据,由容器负责实例化、装配和管理这些组件

二、对 IoC 的深入理解

如果初次理解 IoC,可能会觉得很困难,可以通过下面的例子帮助我们理解 IoC 的作用。

假如,现在需要使用程序来模拟一个简单制造汽车的过程,其实现思路如下:

  • 如果要造汽车Car,首先需要有车身Framework;
  • 如果需要造车身Framework,首先就需要有地盘Bottom;
  • 如果需要造地盘Bottom,首先就需要有轮子Tire。

上述过程,不难发现存在一个依赖链的关系,首先通过传统程序的开发方式,来感受一下其存在的问题。

2.1 传统程序开发方式存在的问题

下面是用传统方式编写的代码:

class Tire {
    private Integer size = 17;

    public void init(){
        System.out.println("do tire: size = " + size);
    }
}

class Bottom {
    private Tire tire;
    public Bottom(){
        this.tire = new Tire();
    }

    public void init(){
        tire.init();
        System.out.println("do bottom");
    }
}

class Framework {
    private Bottom bottom;
    public Framework(){
        bottom = new Bottom();
    }

    public void init(){
        bottom.init();
        System.out.println("do framework");
    }
}

 class Car {
    private Framework framework;
    public Car(){
        framework = new Framework();
    }

    public void init(){
        framework.init();
        System.out.println("do car");
    }
}


public class Tradition {
    public static void main(String[] args) {
        Car car = new Car();
        car.init();
    }
}

在上面的传统写法中,可以发现每个类之间的耦合度非常高,并且每个类都需要管理它所依赖的对象,即掌握着其依赖对象的控制权。此时如果用户的需求改变了,使用的轮子Tire不再是固定的尺寸,而是需要由用户自己输入尺寸的大小,此时Tire类的代码需要更改为:

class Tire {
    private Integer size = 17;

    public Tire(int size){
        this.size = size;
    }

    public void init(){
        System.out.println("do tire: size = " + size);
    }
}

此时发现,不改不要紧,但是一改问题就来了,由于各个类之间的高耦合性,使得后续所依赖前者的代码都需要进行修改。


class Bottom {
    private Tire tire;
    public Bottom(int size){
        this.tire = new Tire(size);
    }

    public void init(){
        tire.init();
        System.out.println("do bottom");
    }
}

class Framework {
    private Bottom bottom;
    public Framework(int size){
        bottom = new Bottom(size);
    }

    public void init(){
        bottom.init();
        System.out.println("do framework");
    }
}

 class Car {
    private Framework framework;
    public Car(int size){
        framework = new Framework(size);
    }

    public void init(){
        framework.init();
        System.out.println("do car");
    }
}

如果要后续要继续增加 Tire 类的属性,例如color,此时就还要需要从头来进行修改。那么如何解决这个缺陷呢?

上次代码出现这个缺陷的根本原因就在于:当前类将自己所依赖的类的控制权掌握在了自己的手中,即在自己类的内部调用的所依赖类的构造方法。因此解决这个问题的方法也非常简单,那就是不在自己的内部代码中创建所依赖的对象,而是通过参数传递的方式获取这个依赖对象,这就是控制权转移的思想,即 IoC。

2.2 控制反转式程序的开发

下面是转移控制权的代码写法,即将当前类所依赖的对象通过参数的方式进行传入:

class Tire {
    private Integer size = 17;

    public Tire(int size) {
        this.size = size;
    }

    public void init() {
        System.out.println("do tire: size = " + size);
    }
}


class Bottom {
    private Tire tire;

    public Bottom(Tire tire) {
        this.tire = tire;
    }

    public void init() {
        tire.init();
        System.out.println("do bottom");
    }
}

class Framework {
    private Bottom bottom;

    public Framework(Bottom bottom) {
        this.bottom = bottom;
    }

    public void init() {
        bottom.init();
        System.out.println("do framework");
    }
}

class Car {
    private Framework framework;

    public Car(Framework framework) {
        this.framework = framework;
    }

    public void init() {
        framework.init();
        System.out.println("do car");
    }
}

public class IoC {
    public static void main(String[] args) {
        Tire tire = new Tire(18, "red");
        Bottom bottom = new Bottom(tire);
        Framework framework = new Framework(bottom);
        Car car = new Car(framework);
        car.init();
    }
}

此时,如果用户需求又发生了改变,要求轮子 Tire 的颜色也要自己来挑选,此时更改的代码如下:

class Tire {
    private Integer size = 17;
    private String color;

    public Tire(int size, String color) {
        this.size = size;
        this.color = color;
    }

    public void init() {
        System.out.println("do tire: size = " + size + ", color = " + color);
    }
}

此时,只需要修改 Tire 类和创建 Tire 对象的代码,而不需要对其他代码进行修改。可以发现,即使底层类的改变也不会影响到整个调用链的改变,这样就实现了代码的解耦,从而实现了更加灵活、通用的程序设计方式。

2.3 对比总结

以下是传统代码和 IoC 形式代码的调用过程:

通过对比两种代码的实现方式不难发现:

  1. 两种代码中类的创建顺序是相反的:
    • 传统代码的创建顺序是由顶层 Car 类创建到底层 Tire 类,即:Car -> Framework -> Bottom -> Tire
    • 而 IoC 形式代码的创建顺序则是由底层 Tire 类创建到顶层 Car 类,即:Tire -> Bottom -> Frame -> Car
  2. 传统代码中,当前类掌握着自己所依赖对象的控制权,耦合度非常高,一旦底层代码修改了,整个调用链的代码都会修改
  3. IoC 形式的代码中,当前类通过参数传递的方式,将对所依赖对象的控制权进行反转,即不再有自己掌控,此时无论底层代码如何修改,也不会对整个调用链上的代码造成影响

三、对 Spring IoC 的理解

“Spring 是一个 包含众多工具的 IoC 容器” 这句话的理解非常重要,因为它揭示了 Spring 框架最核心的功能和优势。在这里,我们可以把 Spring IoC 容器比喻成一个大大的容器,用来存放应用程序中的各种对象(Bean)。这个容器不仅负责对象生命周期的管理,还实现了对象之间的依赖注入(DI),从而实现了控制反转。

既然 Spring 是容器,那么它的核心功能就可以分为两个:

1. 将对象存入容器

  • 程序员首先需要创建应用程序中的各种对象(Bean),这些对象代表应用程序中的不同组件和功能模块。
  • 然后通过配置文件(如XML、注解或者 Java 配置类)告诉 Spring IoC 容器如何创建这些对象,以及它们之间的依赖关系。
  • Spring IoC 容器会根据配置信息,在应用程序启动的时候负责实例化这些对象并存放到容器中,从而完成对象的创建和组装过程。

2. 从容器中取出对象

  • 一旦对象被存入了 Spring IoC 容器中,它们就可以在应用程序的其他部分被获取和使用。程序员不需要显示地创建对象,而是从 Spring 容器中直接获取已经创建好的对象来使用。
  • 通过依赖注入的方式,Spring IoC 容器会在合适的时候将依赖的对象自动注入到需要它们的地方。

总的来说,Spring IoC 容器负责管理对象的创建、组装和依赖关系,开发人员只需关注对象的定义和配置通过将对象存入容器并从容器中获取对象,Spring 实现了对象的控制反转,使得应用程序的开发更加简洁、灵活和易于维护。这也是 Spring 框架的核心之一,为开发者提供了一个强大且高度可定制的开发平台。

四、DI 的概念

4.1 什么是 DI

依赖注入(Dependency Injection,DI)是一种实现 IoC 的具体技术,它是 IoC 容器的一种表现形式。在 DI 中,对象之间的依赖关系不再有类自己创建和管理,而是有外部容器(如 Spring)来负责注入依赖的对象。简单来说,DI 就是将一个对象的依赖关系交给外部容器来处理,从而实现对象之间的解耦。

在 DI 中,通常由三种注入方式:

  1. 构造方法注入(Constructor Injection):通过构造函数接收依赖对象,这是最常见的注入方式。
  2. Setter 方法注入(Setter Injection):通过Setter方法设置依赖对象。
  3. 属性注入(Field Injection):通过属性的方式注入依赖对象。

DI 使得对象的依赖关系从代码中移出,变得可配置和灵活。通过使用DI,我们可以在应用程序的不同部分之间实现松耦合,提高代码的可测试性、可维护性和可扩展性。

4.2 DI 与 IoC的关系

DI(依赖注入)和 IoC(控制反转)是紧密相关的概念,通常同时被提及,它们之间的关系可以概括为:

  • IoC 是一种设计思想,它将应用程序的控制权从代码内部转移到外部容器。通过 IoC,对象的创建和依赖关系的管理被反转,交由容器来负责管理,而不是由代码显示的控制。
  • DI 是实现 IoC 的一种具体技术手段,它是 IoC 的一种表现形式。通过 DI,对象之间的依赖关系由外部容器来注入,而不是由对象自己来创建和管理依赖。

因此,DI 是 IoC 的一部分,它是实现 IoC 的重要手段。Spring 框架正是以 IoC 和 DI 为核心,提供了强大的 IoC 容器和依赖注入机制,从而实现了各种功能,如依赖管理、AOP、事务管理等。通过 IoC 和 DI,Spring 框架实现了松耦合、可配置和可扩展的应用程序开发。

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

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

相关文章

flutter:Future、Stream、RxDart

Future 在Flutter中,Future是Dart语言中的一个类,用于表示异步操作的结果。与Future相关的的重要关键字包括async和await。 async:这个关键字用于在方法或函数声明前添加,以指示该方法为异步方法。在异步方法中,执行…

c++画出分割图像,水平线和垂直线

1、pca 找到图像某个区域的垂直线&#xff0c;并画出来 // 1、 斑块的框 血管二值化图&#xff0c;pca 找到垂直血管壁的直线, 还是根据斑块找主轴方向吧// Step 1: 提取斑块左右范围内的血管像素点坐标&#xff0c;std::vector<cv::Point> points;for (int y 0; y <…

用Apache Echarts展示数据

目录 1.后端代码 1.1 实体类&#xff1a; 1.2 SQL语句&#xff1a; 2.前端代码 2.1 安装 Apach Echarts安装包&#xff1a; 2.2 查找数据并赋值给Echarts 思路&#xff1a;后端查到数据&#xff0c;包装为map&#xff0c;map里有日期和每日就诊人数&#xff0c;返回给前端…

异或运算详解

异或运算详解 定义特性用途总结 定义 参与运算的两个数据,按二进制位进行 ^ 运算,如果两个相对应为值相同结果为0,否则为1 1 ^ 0 1 0 ^ 1 1 0 ^ 0 0 1 ^ 1 0特性 异或^运算只能用于数值(整数) x ^ 0 x x ^ x 0用途 两个值交换,而不用使用临时变量 a a ^ b; b b ^…

css在线代码生成器

这里收集了许多有意思的css效果在线代码生成器适合每一位前端开发者 布局&#xff0c;效果类&#xff1a; 网格生成器https://cssgrid-generator.netlify.app/ CSS Grid Generator可帮助开发人员使用CSS Grid创建复杂的网格布局。网格布局是创建Web页面的灵活和响应式设计的强…

【Linux】在服务器上创建Crontab(定时任务),自动执行shell脚本

业务场景&#xff1a;该文即为上次编写shell脚本的姊妹篇,在上文基础上,将可执行的脚本通过linux的定时任务自动执行,节省人力物力,话不多说,开始操作! 一、打开我们的服务器连接工具 连上服务器后,在任意位置都可以执行:crontab -e 如果没有进入编辑cron任务模式 根据提示查看…

Day01-作业(HTMLCSS)

作业1&#xff1a;通过HTML的标签及CSS样式&#xff0c;完成如下企业简介html页面的制作 A. 最终效果如下&#xff1a; B. 文字素材如下&#xff1a; 企业简介传智教育(股票代码 003032)&#xff0c;隶属江苏传智播客教育科技股份有限公司&#xff0c;注册资本4亿元&#xff0…

国产GOWIN实现低成本实现CSI MIPI转换DVP

CSI MIPI转换DVP&#xff0c;要么就是通用IC操作&#xff0c;如龙讯芯片和索尼芯片&#xff0c;但是复杂的寄存器控制器实在开发太累。对于FPGA操作&#xff0c;大部分都是用xilinx的方案&#xff0c;xilinx方案成本太高&#xff0c;IP复杂。 而用国产GOWIN已经实现了直接mipi …

腾讯云TencentOS Server镜像系统常见问题解答

腾讯云TencentOS Server镜像是腾讯云推出的Linux操作系统&#xff0c;完全兼容CentOS生态和操作方式&#xff0c;TencentOS Server操作系统为云上运行的应用程序提供稳定、安全和高性能的执行环境&#xff0c;TencentOS可以运行在腾讯云CVM全规格实例上&#xff0c;包括黑石物理…

jmeter之接口测试(http接口测试)

基础知识储备 一、了解jmeter接口测试请求接口的原理 客户端--发送一个请求动作--服务器响应--返回客户端 客户端--发送一个请求动作--jmeter代理服务器---服务器--jmeter代理服务器--服务器 二、了解基础接口知识&#xff1a; 1、什么是接口&#xff1a;前端与后台之间的…

【设计模式】单例模式

什么是单例模式&#xff1f; 保证一个类仅有一个实例&#xff0c;并提供一个访问它的全局访问点 单例模式的应用场景 1.整个程序的运行中只允许有一个类的实例&#xff1b; 2.需要频繁实例化然后销毁的对象。 3.创建对象时耗时过多或者耗资源过多&#xff0c;但又经常用到…

Python web实战 | 使用 Flask 实现 Web Socket 聊天室

概要 今天我们学习如何使用 Python 实现 Web Socket&#xff0c;并实现一个实时聊天室的功能。本文的技术栈包括 Python、Flask、Socket.IO 和 HTML/CSS/JavaScript。 什么是 Web Socket&#xff1f; Web Socket 是一种在单个 TCP 连接上进行全双工通信的协议。它是 HTML5 中的…

Flutter Flar动画实战

在Flare动面出现之前,Flare动画大体可以分为使用AnimationController控制的基础动画以及使用Hero的转场动画,如果遇到一些复杂的场景,使用这些动画方案实现起来还是有难度的。不过,随着Flutter开始支持Flare矢量动面,Flutter的动画开发也变得越来越简单。事实上,Flare动画…

2024考研408-计算机网络 第三章-数据链路层学习笔记

文章目录 前言一、数据链路层的功能1.1、数据链路层的研究思想1.2、数据链路层基本概念1.3、数据链路层功能概述&#xff08;5个功能&#xff09; 二 、组帧2.1、封装成帧以及发送帧的过程&#xff08;包含名词解释&#xff09;2.2、实现透明传输及四种组帧方法2.2.1、什么是透…

为什么马斯克和奥特曼都想重振加密货币?

1、前言 加密货币已经死了吗&#xff1f;这个问题的答案取决于谁来回答。一个加密爱好者会给你一百个不同的理由来解释为什么加密货币没有死。特斯拉CEO埃隆马斯克和OpenAI CEO 山姆奥特曼都对加密货币及其在塑造未来世界中的潜在作用有着浓厚的兴趣。 在过去很长一段时间里&…

涛思数据与拾贝云达成战略合作,携手赋能工业数字化转型

2023 年 7 月 27 日&#xff0c;北京涛思数据科技有限公司&#xff08;以下简称“涛思数据”&#xff09;与广州拾贝云科技有限公司&#xff08;以下简称“拾贝云”&#xff09;于广州签署战略合作协议。双方围绕电力行业的需求与痛点展开积极讨论&#xff0c;就如何量身打造最…

ZKML——EZKL团队分享

1. 引言 “ZKP之于数字签名” 类似于 “以太坊之于比特币”&#xff1a; 所谓数字签名&#xff0c;是指&#xff1a;“我知道某秘密secrets&#xff0c;使得 F(secrets, public inputs)pubic outputs”&#xff0c;其中F为fixed function&#xff08;固定函数&#xff09;。这…

ssl单向证书和双向证书校验测试及搭建流程

零、前提准备 首先了解下HTTP和HTTPS的区别&#xff1a; HTTPS与HTTP有什么不同&#xff1f; HTTP是过去很长一段时间我们经常用到的一种传输协议。HTTP协议传输的数据都是未加密的&#xff0c;这就意味着用户填写的密码、账号、交易记录等机密信息都是明文&#xff0c;随时…

SpringBoot项目修改中静态资源,只需刷新页面无需重启项目(附赠—热加载)

初衷 &#x1f4a2;初衷&#x1f4a2; 因为一遍遍修改并重启项目觉得很麻烦&#xff0c;所以刚开始就自己给项目配置了热加载&#xff0c;但奈何代码更新还是慢&#xff0c;还不如我重启一遍项目的速度&#xff0c;所以放弃了自己上网找到的热加载配置。直到我debugger前端代码…

ELFK——ELK结合filebeat日志分析系统(2)

目录 一、filebeat 二、ELFK 1.原理简介 2.在ELK基础上部署filebeat 一、filebeat Filebeat&#xff0c;轻量级的开源日志文件数据搜集器。通常在需要采集数据的客户端安装 Filebeat&#xff0c;并指定目录与日志格式&#xff0c;Filebeat 就能快速收集数据&#xff0c;并…