Spring IoC的基本概念

引言

在 Java 中,出现了大量轻量级容器,这些容器有助于将来自不同项目的组件组装成一个有凝聚力的应用程序。这些容器的底层是它们如何执行布线的常见模式,它们将这一概念称为“控制反转”。


🏢 本章内容

🏭 IoC服务理念;

🏭 让别人为你服务;

🏭 IoC的附加值;


IoC服务理念

IoCInversion of Control,即_“控制反转”_,不是什么技术,而是一种设计思想。其核心理念是从传统的程序设计中将控制权逆转,通过外部容器来管理对象的创建、组装和生命周期,从而降低代码之间的耦合度,提高系统的灵活性和可维护性。

在实际应用中,IoC 的实现方式主要包括依赖注入(Dependency Injection,DI)和依赖查找(Dependency Lookup)两种方式。

🗡️ 依赖注入:

是一种常见的实现方式,通过构造函数、方法参数、或者特定的注入接口,将对象所依赖的其他对象交由外部容器负责创建和管理,而不是在对象内部通过 new 关键字主动创建。这样一来,对象的依赖关系由外部容器来维护,对象本身不需要关心依赖对象的创建和生命周期,从而实现了解耦合。

🗡️ 依赖查找:

是另一种IoC的实现方式,它通过容器提供的API,允许对象查询(或查找)所依赖的其他对象。对象在需要依赖对象时,向容器发出查询请求,容器则负责返回相应的依赖对象。这种方式相对于依赖注入来说更加灵活,但也增加了代码的复杂度和耦合度。

理解 Ioc 的要点

🥋 谁控制谁,控制什么

理解IoC的关键在于颠覆传统程序设计中对象间的关系模式。传统模式中,对象之间的依赖关系由对象自身负责管理和创建,而IoC则是将这种控制权交给外部容器,实现了对象间的松耦合。这种“谁控制谁,控制什么”的转变,使得代码更加清晰、可扩展、易维护。

🥋 为何是反转,哪些方面反转了

为什么称之为“反转”呢?因为在IoC中,传统的对象创建和管理流程被颠倒过来了。传统情况下,对象自身主动创建和管理依赖对象,而在IoC中,对象成为被动的接受者,由外部容器负责创建、管理和注入依赖对象。因此,IoC被称为一种“反转”,它颠覆了传统程序设计中对象间的控制关系。

举个栗子

🥊 **你口渴想喝水吧?**以前,你得自己找到水(依赖对象),然后自己到杯子里,然后再喝水。

而现在,你只要跟你的“另一半” 使个眼色或说一句“Honey,水拿来。” 她就会心领神会地到饮水机那里为你接杯水,然后再给喂你喝下。(此时此刻,你心里肯定窃喜,“有人照顾的感觉真好!”)对你来说,到底哪种场景比较惬意,我想已经不言自明了吧?

🥊 使用前:

请在此添加图片描述

🥊 使用后:

请在此添加图片描述

让别人为你服务

🔫 “先生,晚上好,欢迎光临XXX,拖鞋手牌拿好,楼上请,男宾一位 。” 当你开着凯迪拉克来到洗浴中心,想要洗澡的时候,通常会直接招呼服务生,让他为你送来拖鞋手牌。同样地,作为被注入对象,要想让IoC为其提供服务,并将所需要的被依赖对象送过来,也需要通过某种方式通知对方

请在此添加图片描述

🧟 如果你是洗浴中心的常客,或许你刚进门,服务生已经将你最常使衣柜,手牌放到了你面前;

🧟 如果你是初次或偶尔光顾,也许你坐下之后还要招呼服务生,“给我拿双拖鞋和手牌”;

🧟 还有一种可能,你根本就不知道的时候,这时,你只示意一下告诉服务生你到底想要什么。

不管怎样,你终究会找到一种方式来向服务生表达你的需求,以便他为你提供适当的服务。那么,在IoC模式中,被注入对象又是通过哪些方式来通知IoC Service为其提供适当服务的呢?

🏹 IoC模式最权威的总结和解释,应该是Martin Fowler的那篇文章 Inversion of Control Containers and the Dependency Injection pattern ,其中提到了三种依赖注入的方式,即构造方法注入(constructor injection)、 setter方法注入(setter injection)以及接口注入(interface injection)。

🪓 构造方法注入(Constructor Injection)

这种方式通过在对象实例化时传递依赖项来完成注入。在洗浴中心中,你作为常客,通过注册时填写的偏好信息,告知洗浴中心你需要的沐浴露和洗发水。当你到达时,洗浴中心已经根据你的偏好提前准备好了相应的产品,直接为你提供服务。构造方法注入的优点在于依赖项的一致性和可靠性。

 // 构造方法注入public BathCenter(BathProduct bathProduct) {this.bathProduct = bathProduct;}

🪓 Setter方法注入(Setter Injection)

这种方式通过调用对象的setter方法来完成依赖项的注入。对于偶尔光顾的顾客而言,他们在到达洗浴中心后,需要向服务员说明他们的洗浴偏好。服务员根据顾客的需求,调用相应的setter方法设置洗浴服务,使得顾客能够享受到与常客相同的洗浴体验。Setter方法注入的灵活性允许在运行时动态地更改依赖项。

// Setter方法注入public void setBathProduct(BathProduct bathProduct) {this.bathProduct = bathProduct;}

🪓 接口注入(Interface Injection)

这种方式通过定义接口来完成依赖项的注入。对于新来的游客而言,他们可能并不清楚洗浴中心提供的服务项目,也不了解适合自己的沐浴产品。在这种情况下,游客通过与服务员的交流来表达自己的需求,服务员根据游客的需求来为其安排相应的服务。接口注入允许通过抽象的接口定义来解耦组件之间的依赖关系,提高了系统的灵活性和可维护性。

// 沐浴产品接口
interface BathProduct {void use();
}// 沐浴中心类
class BathCenter {private BathProduct bathProduct;// 构造方法注入public BathCenter(BathProduct bathProduct) {this.bathProduct = bathProduct;}// Setter方法注入public void setBathProduct(BathProduct bathProduct) {this.bathProduct = bathProduct;}// 提供洗浴服务public void provideBathService() {System.out.println("欢迎光临洗浴中心!");bathProduct.use(); // 使用沐浴产品System.out.println("感谢您的光临,祝您洗浴愉快!");}
}// 沐浴露类
class ShowerGel implements BathProduct {@Overridepublic void use() {System.out.println("使用沐浴露洗澡");}
}// 洗发水类
class Shampoo implements BathProduct {@Overridepublic void use() {System.out.println("使用洗发水洗头");}
}// 测试类
public class Main {public static void main(String[] args) {// 构造方法注入BathProduct showerGel = new ShowerGel();BathCenter bathCenter1 = new BathCenter(showerGel);bathCenter1.provideBathService();// Setter方法注入BathProduct shampoo = new Shampoo();BathCenter bathCenter2 = new BathCenter(null); // 初始化时不注入沐浴产品bathCenter2.setBathProduct(shampoo);bathCenter2.provideBathService();// 接口注入BathCenter bathCenter3 = new BathCenter(null); // 初始化时不注入沐浴产品bathCenter3.setBathProduct(new BathProduct() {@Overridepublic void use() {System.out.println("使用默认沐浴产品洗澡");}});bathCenter3.provideBathService();}
}

三种注入方式的比较

在IoC模式中,构造方法注入、Setter方法注入和接口注入是三种常见的依赖注入方式。它们在使用场景、灵活性和实现复杂度上有一些区别,以下是它们的比较:

🛡️ 构造方法注入(Constructor Injection)

  • 使用场景: 适用于在对象创建时就确定其依赖关系的情况。常用于注入不变的依赖项,例如配置信息、常用服务等。
  • 灵活性: 由于依赖项在对象创建时确定,因此一旦对象被创建,其依赖关系就不可更改。这可能会限制一些动态变化的需求。
  • 实现复杂度: 相对简单,不需要提供额外的setter方法用于动态修改依赖。

🛡️ Setter方法注入(Setter Injection)

  • 使用场景: 适用于依赖项可能在对象的生命周期内发生变化的情况。常用于可变的依赖关系,允许在运行时动态修改。
  • 灵活性: 具有较高的灵活性,可以在任何时候通过调用setter方法来修改依赖关系。适用于需要动态切换依赖的情况。
  • 实现复杂度: 相对较复杂,需要提供额外的setter方法,并在对象创建后可能需要进行额外的检查来确保正确的依赖关系。

🛡️ 接口注入(Interface Injection)

  • 使用场景: 适用于希望通过共享接口来解耦依赖项的情况。可以用于将依赖注入抽象为接口,具体实现由外部注入。
  • 灵活性: 具有一定的灵活性,可以通过不同的接口实现来注入不同的依赖。更适合于对接口较为关心的情况。
  • 实现复杂度: 需要定义接口并提供不同的实现类。相对于构造方法注入和Setter方法注入,实现上略显繁琐。

IoC的附加值

IoC模式的附加值远不止于简单的依赖注入,它带来的好处包括更低的耦合度、更好的可测试性、可重用性和可扩展性等。让我们通过具体的示例来深入理解IoC模式的附加值。

降低耦合度

在传统的程序设计中,对象之间的依赖关系通常是硬编码的,对象直接实例化并使用其依赖对象。这种做法会导致高耦合度,当一个对象发生变化时,可能需要修改其他多个对象。而使用IoC模式,对象不再负责获取依赖对象,而是通过外部容器进行管理和注入,从而降低了对象之间的耦合度。

提高可测试性

IoC模式使得依赖对象的注入变得灵活,可以通过Mock对象等方式轻松替换真实的依赖对象,从而方便进行单元测试。单元测试可以针对对象的特定行为进行测试,而不受其依赖对象的影响,提高了测试的精度和可靠性。

增强可重用性

通过将依赖对象与实现解耦,使得依赖对象可以被多个对象共享和重用。这样一来,我们可以更轻松地将已经实现和测试过的组件应用到新的场景中,而不必重复开发和测试相同的功能。

支持可扩展性

IoC模式使得系统更容易扩展,新的功能模块可以通过依赖注入的方式加入到系统中,而不必修改现有的代码。这种松耦合的设计使得系统更具弹性,能够更好地适应未来的需求变化和业务扩展。

通过具体的示例,我们可以更直观地感受到IoC模式带来的好处。它不仅仅是一种设计模式,更是一种思想,能够帮助我们构建更灵活、可测试、可维护和可扩展的软件系统。

举个例子

忽然有一天,洗浴中心老板为了能够洗浴更多的顾客到店光顾,他茅塞顿开,想到顾客洗完澡后,可以到二楼休息,饮茶。于是提供了,乘电梯上2楼的服务。

请在此添加图片描述

☯️ 如果没有IoC,那么新增的业务是没有办法与顾客进行绑定的,客户并不知道我们提供了这样的服务。功能需要重新实现。

☯️ 如果使用IoC,我们可以主动的告知客户,我们提供了上2楼服务,这样在接口不发生变化的情况,我们可以提供更多的服务方式。

👹 原始提供的服务:

// 洗浴产品接口
interface BathProduct {void use();
}// 沐浴露实现
class ShowerGel implements BathProduct {@Overridepublic void use() {System.out.println("使用沐浴露洗澡");}
}// 洗发水实现
class Shampoo implements BathProduct {@Overridepublic void use() {System.out.println("使用洗发水洗头");}
}// 洗浴中心类
class BathCenter {private BathProduct bathProduct;// 构造方法注入public BathCenter(BathProduct bathProduct) {this.bathProduct = bathProduct;}// 提供洗浴服务public void provideBathService() {System.out.println("欢迎光临洗浴中心!");bathProduct.use(); // 使用洗浴产品System.out.println("感谢您的光临,祝您洗浴愉快!");}
}// 测试类
public class Main {public static void main(String[] args) {// 构造方法注入洗浴露BathProduct showerGel = new ShowerGel();BathCenter bathCenter1 = new BathCenter(showerGel);bathCenter1.provideBathService();// 构造方法注入洗发水BathProduct shampoo = new Shampoo();BathCenter bathCenter2 = new BathCenter(shampoo);bathCenter2.provideBathService();}
}

👻 新增搭乘电梯上2楼服务

// 上2楼服务实现:电梯服务
class ElevatorService implements BathProduct {@Overridepublic void use() {System.out.println("正在搭乘电梯前往2楼");}
}// 测试类
public class Main {public static void main(String[] args) {// 构造方法注入上2楼服务UpstairsService elevatorService = new ElevatorService();BathCenter bathCenter3 = new BathCenter(elevatorService);bathCenter3.provideBathService();}
}

结语

IoC模式不仅仅是一种设计模式,更是一种设计思想,通过它,我们能够构建出更加灵活、可测试和可维护的软件系统。在现实应用中,合理运用IoC模式能够为软件开发带来诸多益处

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

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

相关文章

图神经网络GNN入门

参考教程:A Gentle Introduction to Graph Neural Networks 图神经网络(Graph Neural Networks,GNNs)是一类专门用于处理图结构数据的神经网络,旨在通过节点、边和图的结构信息来学习图中节点和图的表示。GNN通过消息传…

卧式螺旋混合机搅拌机:饲料加工设备

卧式螺旋混合机搅拌机是一种用于饲料混合的设备,其结构特点为卧式,即搅拌桶体水平放置。这种设计使得物料在搅拌过程中能够充分混合,且搅拌效率高、混合均匀度好。卧式饲料混合机广泛应用于畜牧业、养殖业以及饲料加工行业,是饲料…

【北京迅为】iTOP-4412全能版使用手册-第四十二章 驱动注册

iTOP-4412全能版采用四核Cortex-A9,主频为1.4GHz-1.6GHz,配备S5M8767 电源管理,集成USB HUB,选用高品质板对板连接器稳定可靠,大厂生产,做工精良。接口一应俱全,开发更简单,搭载全网通4G、支持WIFI、蓝牙、…

交易系统:线上交易系统流程详解

大家好,我是汤师爷~ 今天聊聊线上交易系统流程详解。 线上交易系统为新零售连锁商家提供一站式线上交易解决方案。其核心目标是,通过数字化手段扩大商家的服务范围,突破传统门店的地理限制。系统支持电商、O2O等多种业务形态,为…

Postman接口测试详解

🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 pre-request script 介绍 在过往的工作中,遇到很多测试小伙伴使用 postman 的时候都是直接通过 api 文档的描述请求,检查返回的数据是否正…

【单链表】(更新中...)

一、 题单 206.反转链表203.移除链表元素 876.链表的中间结点BM8 链表中倒数最后k个结点21.合并两个有序链表 二、题目简介及思路 206.反转链表 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 思路简单,但是除了要两个指针进…

深入理解 SQL 注入:原理、攻击流程与防御措施

深入理解 SQL 注入:原理、攻击流程与防御措施 在当今数字化的时代,数据安全已成为每个企业和开发者必须面对的重要课题。SQL 注入(SQL Injection)作为一种常见的网络攻击方式,给无数企业带来了巨大的损失。本文将深入…

市场上显卡型号需求分析

两个平台统计:(关键词统计,仅做参考) GPU型号|平台 github(提交量/万) huggingface(模型量/个) H100 6.6 210 A100 17.2 483 V100 14.4 484 4090 27.3 31 3090 11.1 92 在git…

C# WPF抽奖程序

C# WPF抽奖程序 using Microsoft.Win32; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.…

Master EDI 项目需求分析

Master Electronics 通过其全球分销网络,支持多种采购需求,确保能够为客户提供可靠的元件供应链解决方案,同时为快速高效的与全球伙伴建立合作,Master 选择通过EDI来实现与交易伙伴间的数据传输。 EDI为交易伙伴之间建立了一个安…

基于单片机的输液速度监控系统设计

本设计是以STM32F103C8T6单片机为控制核心,用户可通过按键模块来设置液体高度与点滴速度的阈值,采用液位传感器实时监测瓶内液体位置,若液位低于所设阈值,蜂鸣器进行声音报警提醒患者或医生。采用步进电机通过控制输液管直径大小从…

河工oj新生周赛第八周2024

A.小七的作业 小柒的作业 - 问题 - 软件学院OJ 代码 #include<bits/stdc.h> using namespace std;int main() {string s;cin >> s;int l, r;cin >> l >> r;string str s.substr(l,r-l1);cout << str;return 0; } B.小七的签到题 小柒的签到…

FPGA工作原理、架构及底层资源

FPGA工作原理、架构及底层资源 文章目录 FPGA工作原理、架构及底层资源前言一、FPGA工作原理二、FPGA架构及底层资源 1.FPGA架构2.FPGA底层资源 2.1可编程输入/输出单元简称&#xff08;IOB&#xff09;2.2可配置逻辑块2.3丰富的布线资源2.4数字时钟管理模块(DCM)2.5嵌入式块 …

低功耗蓝牙模块在高尔夫测距仪上的应用

在绿意盎然的高尔夫球场上&#xff0c;每一次挥杆都承载着球员对精准与完美的追求。随着科技的飞速发展&#xff0c;高尔夫运动也迎来了智能化的革新。一款集成了先进蓝牙模组的高尔夫测距仪&#xff0c;它不仅重新定义了高尔夫运动的测距精度&#xff0c;更以无线互联的便捷性…

如何在 cPanel 中创建子域名:分步指南

cPanel 是一个用于管理网站的工具&#xff0c;操作界面简单直观&#xff0c;常用于管理网站的各种功能&#xff0c;包括创建子域名。很多知名的网络服务提供商&#xff0c;如 Hostease&#xff0c;都提供了 cPanel 管理工具。 本文将详细介绍如何在 cPanel 中创建子域名&#x…

减少30%人工处理时间,AI OCR与表格识别助力医疗化验单快速处理

在医疗行业&#xff0c;化验单作为重要的诊断依据和数据来源&#xff0c;涉及大量的文字和表格信息&#xff0c;传统的手工输入和数据处理方式不仅繁琐&#xff0c;而且容易出错&#xff0c;给医院的运营效率和数据准确性带来较大挑战。随着人工智能技术的快速发展&#xff0c;…

Linux安装BellSoft JDK 17 LTS

原来使用的OpenJdk&#xff0c;看到SpringBoot官网推荐&#xff08;如下图&#xff09;贝尔实验室的JDK&#xff0c;打算换一下 官方下载链接 JKD下载 可以看到Win、Mac、Linux都提供了&#xff0c;并且还有x86架构和arm架构的 在Linux中我们可以使用 uname -a 查看当前操作系…

C++(九)

前言&#xff1a; 本文主要讲述运算符的优先顺序。 一&#xff0c;运算符的优先级。 请看以下表达式&#xff1a; a32*5 运算结果为&#xff1a;13. 可以看到&#xff0c;在此代码中&#xff0c;先运行了2*5的结果&#xff0c;在此基础上在进行3操作&#xff0c;因此结果…

学生公寓智能限电系统的功能和作用

学生公寓智能限电系统‌是一种用于管理和限制学生公寓用电的设备和技术&#xff0c;旨在确保用电安全、防止火灾事故&#xff0c;并促进节能减排。以下是关于学生公寓智能限电系统的详细介绍&#xff1a; 1、功能和作用 智能限电系统通过以下功能来管理和限制用电&#xff1a…

嵌入式入门Day25

数据结构Day 6,IO Day1 查找算法顺序查找折半查找&#xff08;二分查找&#xff09;哈希查找 IO概念标准IO创建递归索引&#xff08;用于查询结构体定义&#xff09; 文件IO标准IO缓冲区指针相关函数 查找算法 顺序查找 关键字&#xff1a;分为主关键字和次关键字主关键字&am…