【Spring篇】初识 Spring IoC 与 DI

news2024/11/25 8:23:04

目录

一.  Spring 是什么 ?

二. 何为 IoC ? 

三. 如何理解 Spring IoC ?

四. IoC 与 DI

五 . 总结


一.  Spring 是什么 ?

    我们通常所说的 Spring 指的是 Spring Framework(Spring 框架),它是⼀个开源框架,有着活跃⽽ 庞⼤的社区,这就是它之所以能⻓久不衰的原因。Spring ⽀持⼴泛的应⽤场景,它可以让 Java 企业级 的应⽤程序开发起来更简单。

  • 以下是 Spring 框架的一些核心特点:
  1. 轻量级:Spring 框架采用了松耦合的设计原则,仅依赖于少量的第三方库,因此它是一个轻量级的框架。开发人员可以根据需要选择使用 Spring 的特定功能,而无需引入整个框架。
  2. 控制反转(IoC):Spring 框架通过控制反转(IoC)容器管理应用程序中的对象及其依赖关系。通过 IoC 容器,开发人员可以将对象的创建、组装和生命周期管理交给 Spring 框架处理,从而实现了松耦合和可测试性。
  3. 面向切面编程(AOP):Spring 框架支持面向切面编程,可以通过 AOP 在应用程序中实现横切关注点的模块化。例如,日志记录、事务管理和安全性等横切关注点可以通过 AOP 进行集中处理,而不会侵入业务逻辑的代码。
  4. 声明式事务管理:Spring 框架提供了声明式事务管理的支持。通过使用注解或 XML 配置,开发人员可以将事务管理逻辑与业务逻辑分离,并且可以轻松地在方法或类级别上应用事务。
  5. 框架整合:Spring 框架可以与许多其他开源框架和技术无缝集成,如 Hibernate、MyBatis、JPA、Struts 和 JSF 等。这使得开发人员可以使用 Spring 框架来整合和协调不同的技术,构建全面的企业应用程序。
  6. 测试支持:Spring 框架提供了广泛的测试支持,包括单元测试和集成测试。它提供了一个专门的测试上下文,可以轻松地编写和执行单元测试,以验证应用程序的行为和功能

如果要使用一句话来概括 Spring , 那就是 Spring是包含了众多工具方法的 IoC 容器 

何为 IoC ? ,  IoC 的全称叫做 Inversion of Control , 即为控制反转 ,也就是说 Spring 是一个控制反转的容器 , 那么怎样理解上述的内容呢 ?

二. 何为 IoC ? 

准确来说 , IoC 指的并不是一种具体的技术, 而是一个实现对象解耦的思想 

耦合 : 当两个或者两个以上的对象存在依赖,一方修改之后就会影响另外一方,就说明这两个对象之间存在这耦合关系 , 而解耦, 就是为了解除这两个对象之间的依赖关系。

那么 IoC 是如何实现对象的解耦呢 ? 

下面通过一个具体的示例来进行讲解 :
       在传统的开发思想中 , 当我们要构建一辆车时 , 而车又依赖于车身, 车身又依赖于底盘,底盘又依赖于轮胎,他们之间的依赖关系如下图所示 :

 当我们使用代码进行实现时 ,所有类之间的调用链如下所示:

// Car类
public class Car {
    // car 进行初始化时 , 需要调用 framework
    public void init(){
        System.out.println("do car");
        Framework framework = new Framework();
        framework.init();
    }
    public static void main(String[] args){
        Car car = new Car();
        car.init();
    }
}
// Framework类
public class Framework {
    // framework 进行初始化时, 调用了Bottom
    public void init(){
        System.out.println("do framework");
        Bottom bottom = new Bottom();
        bottom.init();
    }

}
// Bottom 类
public class Bottom{
    // Bottom 进行初始化时,调用了 Tire
    public void init(){
        System.out.println("do bottom");
        Tire tire = new Tire();
        tire.init();
    }
}
// Tire 类
public class Tire {
    private int size = 10;
    public void init(){
        System.out.println("size -> "+size);
    }
}

在上述的代码当中,如果轮胎的尺寸 size 发生了改变,相对应的 底盘,车身,再到整车都要发生一定的变化,也就是当我们在自身类创建下级类时,当下级类发生改变操作,自己也要跟着修改,代码的耦合程度较高,不利于后续的更新和添加元素,所以如何实现程序的解耦呢?

那么如何解决传统开发中的缺陷呢 ?

答案是我们在创建类时,不在每个类中创建下级类,而是改为传递的方式(也就是注入的方式),因为我们不需要在当前类中创建下级类了,所以下级类中即使发生变化,当前类也无需修改任何代码,这样就完成了程序的解耦。

下面我们将参数传递的方式进行修改:

  • 通过将对象进行传递而并非 new 对象的方式来进行解决:
# car类
public class Car {
    private Framework framework;

    // 将 framework 注入 car
    public Car(Framework framework) {
        this.framework = framework;
    }
    public void init() {
        System.out.println("do car...");
        framework.init();
    }
}
# framework 类
public class Framework {
    private Bottom bottom;
     // bottom 注入 framework
    public Framework(Bottom bottom){
        this.bottom = bottom;
    }

    public void init(){
        System.out.println("do framework");
        bottom.init();
    }
}
# bottom类
public class Bottom {
    private Tire tire;
   // tire 注入 bottom
    public Bottom(Tire tire) {
        this.tire = tire;
    }

    public void init() {
        System.out.println("do bottom...");
        tire.init();
    }
}
# trie 类
package newCar;

public class Tire {
    private int size = 17;
    private String color = "红色";

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

    public void init() {
        System.out.println("size -> " + size + " | color -> " + color);
    }
}
# 测试类
public class Test {
    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.init();
    }
}

代码经过上述的调整之后,无论底层的类如何进行改变,整个调用链都不会发生变化,这样就完成了代码之间的解耦。

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

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

再举一个开发中比较简单的例子 :
当我们在开发过程中, A 对象要使用 B对象的 某个方法, 我们通常的实现方法如下 :

class A{
   public void init(){
    // new一个B类对象,调用 init()方法
     B b = new B();
     b.init();
  }
}
class B{
   public B(){

  }
  public void init(){
    System.out.println("hello world!");
  }
}

此时,A 和 B对象是相互进行耦合的, 当修改了B中构造方法的参数时,A对象也要随之发生改变。

class B {
   public B(String name){
       System.out.println("姓名 :"+name);
  }
  public void init(){
       System.out.println("Hello word!");
  }
}

 那么实际业务中,如何解决上述的问题呢?

与上一个例子类似, 也是通过将对象进行传递(注入)的方式 , 代码如下 :

class A {
    // 先定义一个需要依赖的 B 对象
    private B b;
    // 通过构造方法实现赋值(初始化)
    public A(B b) {
        this.b = b;
    }
    public void init() {
        // 调用 B 类中的 init 方法
        b.init();
    }
}
class B {
    public B(String name) {
        System.out.println("姓名:" + name);
    }
    public void init() {
        System.out.println("Hello World!");
    }
}

这样修改之后,无论B的构造方法怎么修改,而调用他的A类不需要做任何修改 ,这样就实现了对象的解耦,那么对象的解耦 和 IoC 又有什么关系呢?

通过上边的两个例子,究其原因还是控制权反转的问题 ,A对象对于B对象的管理权交出,交给其他程序来管理,此时A对象对于B对象的管理权发生了反转和改变,这就是 IoC 

三. 如何理解 Spring IoC ?

在 Spring 中, 将 对象交给了 IoC 容器来进行管理 , 不需要关注怎么去创建对象,而是关注创建对象之后的操作, 把对象的创建,初始化,销毁交给了 Spring IoC 容器来管理 。

既然 Spring 是一个IoC容器 ,那么他就具有两个最基础的功能 :

  • 将对象存入到容器
  • 从容器中取出对象

也就是说, 对象的创建和销毁都交给了Spring来管理了,它本身又具备了存储对象和获取对象的能力 。

Spring IoC 的优点有哪些 ?

  • 实现对象解耦
  • 使用更加方便(不需要手动去new创建,和关注对象背后的依赖问题,只关注使用权)
  • 更加高效(从容器中取出对象,不需要重新new)

四. IoC 与 DI

DI 是 Dependency Injection 的缩写,翻译成中文是“依赖注入”的意思。依赖注入不是一种设计实现,而是一种具体的技术,它是在 IoC 容器运行期间,动态地将某个依赖对象注入到当前对象的技术就叫做 DI(依赖注入)

在上述的两个例子当中,对象的传递通过构造方法被动的获取对象,在Spring 中 DI 的实现一般通过注解来主动的获取对象, IoC 是一种设计思想,而 DI 是一种具体的实现技术,比如想吃顿好的是一种思想,但是吃火锅还是串串是一种具体的实现。

Spring中DI有三种 常用的注入方式,后边会详细介绍,下面简单介绍演示一下属性注入:

@Service
public class FrameWork{
    @Autowired
    private Bottom bottom;//通过属性注入
}

五 . 总结

     IoC 和 DI 都是 Spring 框架中的重要概念,它们都是用来实现对象解耦的,其中 IoC(控制反转)是一种设计思想,而 DI(依赖注入)是一种具体的实现手段,Spring IoC 中最基础的功能就是对对象的存取。


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

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

相关文章

SAP 执行失败JOB及dump日志监控

背景 系统使用时间较长,存在大量的后台作业,用户量较大,需要及时监控后台作业状况及系统dump情况,以便及时处理。 功能 1. 查询屏幕填写日期前n秒状态为错误及未知的后台作业 2. 查询屏幕填写日期前n秒系统中的dump记录--客制化程…

有关动态内存管理的笔试题

题目一: void GetMemory(char* p) {p (char*)malloc(100); }void test(void) {char* str NULL;GetMemory(str);strcpy(str, "hello world");printf(str); }int main() {test();return 0; } 请问上述代码输出结果是什么,理由是什么&#xf…

leetcode 90. 子集 II

2023.7.23 这道题是上一题子集的升级版&#xff0c;即数组nums包含了相同的元素&#xff0c;这时候需要对集合之间进行去重&#xff0c;可以参考这一题组合总和II的去重方法。 下面直接上代码&#xff1a; class Solution { public:vector<vector<int>> ans;vecto…

Qt QToolBar 添加 换行 添加到底部 左侧 右侧

1. 常用添加&#xff1a; #include "mainwindow.h" #include "ui_mainwindow.h" #include <QDebug> MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow) {ui->setupUi(this);QToolBar *toolBar addToolBar(t…

查找和二叉树(基础知识和基本操作)

查找&#xff1a; 1.二分查找&#xff1a;先定一个大范围&#xff0c;想一个数&#xff0c;看是在起始范围到中间范围还是中间范围到结束范围&#xff0c;依次循环直到确定值&#xff08;相当于一直把范围折半&#xff0c;直到找到&#xff09; while(low<high) {int mid(…

一元多项式的表示及相加

实现思路&#xff1a; 通过链表实现&#xff0c;会更为简单直观。用链表中的每个结点表示多项式中的每一项&#xff0c;多项式每一项都是由数据域&#xff08;包含系数和指数&#xff09;和指针域构成的&#xff0c;所以在定义表示结点的结构体时&#xff0c;可如下所示进行定义…

【Ansible 自动化配置管理实践】01、Ansible 快速入门

目录 一、Ansible 快速入门 1.1 什么是 Ansible ​1.2 Ansible 主要功能 1.3 Ansible 的特点 1.4 Ansible 基础架构 二、Ansible 安装与配置 2.1 Ansible 安装 2.2 确认安装 三、Ansible 配置解读 3.1 Ansible 配置路径 3.2 Ansible 主配置文件 3.3 Ansi…

PHP后台登录功能单账号登录限制

PHP后台登录功能单账号登录限制 单账号登陆是什么第一步创建数据表第二步创建登录页面test2.html第三步创建登录提交test2.php第四步访问后台首页第五步演示 单账号登陆是什么 一个用户只能登录一个账号通常被称为单账号登录限制或单用户单账号限制。这意味着每个用户只能使用…

TCP通信 -- 接收并反馈(全)

TCP通信整体过程&#xff1a; 1.【服务端】启动,创建ServerSocket对象&#xff0c;等待连接。2.【客户端】启动,创建Socket对象&#xff0c;请求连接。3.【服务端】接收连接,调用accept方法&#xff0c;并返回一个Socket对象。4.【客户端】Socket对象&#xff0c;获取OutputStr…

JVM之内存与垃圾回收篇3

文章目录 8 垃圾回收8.1 基本理论8.1.1 对象的finalization机制8.1.2 理解System.gc8.1.3 内存溢出和内存泄漏8.1.4 Stop The World8.1.5 安全点和安全区域8.1.6 Java中的引用 8.2 垃圾回收算法8.2.1 引用计数法8.2.2 可达性分析8.2.2.1 使用MAT查看GC Roots8.2.2.2 使用JProfi…

【docker,typeorm】docker时区与本地时区的不同步【已解决】

前言 我使用账号登陆vuecms.cn网站&#xff0c;查看登陆日志&#xff0c;发现所有时间全部少8个小时。懵逼树上懵逼果&#xff0c;懵逼树下你和我… 我的开源网站后端是基于nestjs&#xff0c;数据库使用typeorm进行连接操作 原因分析&#xff1a; 原因一: docker环境与本地环…

结构型设计模式之适配器模式【设计模式系列】

系列文章目录 C技能系列 Linux通信架构系列 C高性能优化编程系列 深入理解软件架构设计系列 高级C并发线程编程 设计模式系列 期待你的关注哦&#xff01;&#xff01;&#xff01; 现在的一切都是为将来的梦想编织翅膀&#xff0c;让梦想在现实中展翅高飞。 Now everythi…

Type [unknown] not present(主要问题是jar冲突)

解决方案&#xff1a;1选择pom.xml 2鼠标移动到打开的pom.xml点击右键选择maven 显示图 3ctrl鼠标左键移动找到红线 可以看到引入冲突 4按照实际需求对pom.xml的引入进行增删或者版本升级降级&#xff0c;直到以下图标中没有红线冲突即可

SQL注入 三范式

学习目标 了解三范式的要求 1. 什么是范式 设计关系数据库时&#xff0c;遵从不同的规范要求&#xff0c;设计出合理的关系型数据库&#xff0c;这些不同的规范要求被称为不同的范式&#xff0c;各种范式呈递次规范&#xff0c;越高的范式数据库冗余越小。 实际上家用电器都有…

香农极限是如何影响光纤容量的

1 引言 上世纪末&#xff0c;DWDM技术开始在干线通信中使用并迅速普及。虽然当时DWDM系统的容量只有402.5G&#xff0c;但实验室中DWDM支持的波道数甚至超过了1000波&#xff0c;单波道速率也飙到了惊人的160G&#xff08;超1000波和单波160G是两个独立事件&#xff09;。人们普…

docker快速搭建并使用Zabbix

docker搭建并使用Zabbix 0 zabbix基础知识 zabbix-server zabbix 的server 端&#xff0c;负责接收agent发送过来的监控数据&#xff0c;并且提供zabbix的所有核心功能。database 用于存储监控数据和配置信息的数据库&#xff0c;目前常用的有mysql和postgresql两种数据库。za…

【AutoSAR应用软件设计】

AutoSAR总体架构 是本文讲解内容。 接口类型 AUTOSAR接口 –SWCs和/或BSW模块交换的信息–独立于实施/网络/硬件–端口接口 标准化AUTOSAR接口 –AUTOSAR接口–标准化的语法和语义–标准化端口接口 标准化接口 –标准化API–通常为特定编程语言&#xff08;“C”&#x…

火狐浏览器鼠标点击页面区域文字时,出现光标的问题

点击一些资源卡片和查看别的页面时&#xff0c;发现点击非输入框的地方&#xff0c;出现了光标&#xff0c;误以为光标处可以填写东西&#xff0c;就试着敲了几次键盘&#xff0c;发现没有任何反应&#xff1b;然后就叫开发那个页面的同事过去看看&#xff1b;那个同事按F12各种…

如何监控Linux和Oracle数据库运行状态

背景: 在生产环境中,一般可能会发生服务器宕机或者数据库宕机的情况,如何准确的把握找准”生产事故“的具体发生时间,其实有很多方法,可以借助第三方的监控软件或者其他收费软件。 但是本人就是穷逼一个,不可能买或者使用盗版的三方软件。所有设计了以下流程检测Linux和…

16_LinuxINPUT子系统

目录 input子系统简 input驱动编写流程 注册input_dev 上报输入事件 input_event结构体 按键input驱动程序编写 编写测试APP 运行测试 input子系统简 按键、鼠标、键盘、触摸屏等都属于输入(input)设备,Linux内核为此专门做了一个叫做input子系统的框架来处理输入事件。…