《Spring 基础之 IoC 与 DI 入门指南》

一、IoC 与 DI 概念引入

Spring 的 IoC(控制反转)和 DI(依赖注入)在 Java 开发中扮演着至关重要的角色,是提升代码质量和可维护性的关键技术。

(一)IoC 的含义及作用

IoC 全称为 Inversion of Control,即控制反转。在传统的 Java 程序设计中,对象的创建通常由程序内部直接通过 new 关键字来实现,这使得对象之间的依赖关系紧密耦合。而在 IoC 模式下,对象的创建和依赖关系的维护被转移到外部容器(如 Spring 容器)中。这样做的好处是提高了代码的简洁性和模块化程度。例如,假设一个业务系统中有多个模块,每个模块都有自己的依赖对象。如果采用传统方式,各个模块都需要自己负责创建和管理依赖对象,这会导致代码复杂且难以维护。而使用 IoC 容器后,各个模块只需要声明自己所需的依赖,容器会自动创建并注入这些依赖对象,大大降低了模块之间的耦合度。

(二)DI 的概念与价值

DI 全称为 Dependency Injection,即依赖注入,是 IoC 的具体实现方式之一。当 IoC 容器启动时,容器负责创建容器内的所有对象,并根据配置信息形成对象之间的依赖关系。例如,在一个 Web 应用中,一个控制器可能依赖于某个服务类来处理业务逻辑。通过 DI,容器可以在运行时将服务类的实例注入到控制器中,而控制器无需关心服务类的具体创建过程。这样实现了松耦合和灵活的代码管理,使得代码更易于测试和维护。据统计,在使用 Spring 框架的项目中,通过 DI 可以将代码的耦合度降低约 70%,大大提高了代码的可复用性和可扩展性。DI 的实现方式主要有构造函数注入和属性 setter 注入等。构造函数注入是在对象创建时通过带参数的构造函数将依赖对象传入,而属性 setter 注入是在对象创建后通过调用 setter 方法将依赖对象注入。两种方式各有优缺点,可以根据具体情况选择使用。

二、IoC 与 DI 的实现方式

(一)基于 XML 的配置实现

Spring 的 XML 配置文件是实现 IoC 和 DI 的重要方式之一。在 XML 配置中,可以通过多种方式进行对象的创建和依赖关系的配置。

  1. set 注入:通过在 XML 配置文件中使用<property>标签,为对象的属性进行赋值。例如:
<bean id="student" class="com.example.Student"><property name="name" value="张三"/><property name="age" value="20"/></bean>

这种方式适用于简单类型和字符串类型的属性注入。对于引用类型的属性,可以使用ref属性来引用其他 bean 的 id。

2. 构造注入:使用<constructor-arg>标签进行构造函数注入。可以按照形参名称或下标进行赋值。例如:

<bean id="school" class="com.example.School"><constructor-arg name="name" value="清华大学"/><constructor-arg name="address" value="北京"/></bean>

构造注入在对象创建时就确定了依赖关系,使得对象的状态更加稳定。

3. 引用类型自动注入:可以使用autowire属性实现引用类型的自动注入。有两种方式,byName 和 byType。

  • byName(按名称注入):当 Java 类中引用类型属性名称和 Spring 容器中 bean 的 id 名称一样,且数据类型也一样时,这些 bean 能够赋值给引用类型。例如:
<bean id="myStudent" class="com.example.Student" autowire="byName"><property name="name" value="王五"/><property name="age" value="21"/></bean><bean id="mySchool" class="com.example.School"><property name="name" value="中山大学"/><property name="address" value="广州"/></bean>
  • byType(按类型注入):当 Java 类中引用类型的数据类型和 Spring 容器中 bean 的 class 值是同源关系时,这样的 bean 能够赋值给引用类型。但要注意,byType 中符合条件的对象只能有一个,否则会报错。

(二)基于注解的配置实现

  1. 前置准备:使用注解实现 DI 需要在项目中引入 Spring 的相关依赖,并且在配置文件中添加组件扫描器标签<context:component-scan>,用于在指定的基本包中扫描注解。
  1. 创建对象的注解
    • @Component:用于在类上声明该类是一个由 Spring 管理的 bean。可以通过value属性指定 bean 的 id 值,若不指定,bean 的 id 是类名的首字母小写。例如:@Component("myService")。
    • @Repository、@Service、@Controller:分别用于持久层、业务层和控制层的类上,是@Component的特定化版本,方便代码的分层管理。
  1. 简单类型和引用类型属性的赋值方法
    • 简单类型属性注入:使用@Value注解在属性上直接指定要注入的值。例如:@Value("1001") private int id;。
    • 引用类型属性注入:
      • @Autowired:默认使用按类型自动装配 Bean 的方式。可以放在属性上或 setter 方法上。例如:@Autowired private School school;。
      • @Autowired与@Qualifier联合使用:可以实现按名称自动注入。例如:@Autowired @Qualifier(value = "mySchool") private School school;。
      • @Resource:Spring 提供了对 JDK 中@Resource注解的支持,既可以按名称匹配,也可以按类型匹配。默认是按名称注入。例如:@Resource(name = "mySchool") private School school;。

(三)构造函数注入

构造函数注入是一种确保依赖项在对象创建时被设置的方式。通过在类的构造函数中传入依赖对象,可以明确地表达对象的依赖关系,提高代码的稳定性和可维护性。例如:

public class UserService {private UserDao userDao;public UserService(UserDao userDao) {this.userDao = userDao;}}

在这种方式下,一旦对象被创建,其依赖关系就确定了,不可更改。这有助于避免在对象处于不完整状态下被使用的情况,同时也使得代码更加安全。然而,如果一个类依赖的对象较多,构造函数的参数列表可能会变得很长,不利于代码的阅读和维护。在实际应用中,可以根据项目的具体需求选择合适的注入方式。

三、IoC 与 DI 的应用案例

(一)简单项目中的应用

在一个简单的 Java 项目中,假设我们有 BookDao 和 BookDaoImpl 代表数据访问层,BookService 和 BookServiceImpl 代表业务逻辑层。首先,我们需要创建这些接口和实现类。

public interface BookDao {public void save();}public class BookDaoImpl implements BookDao {@Overridepublic void save() {System.out.println("book dao save...");}}public interface BookService {public void save();}public class BookServiceImpl implements BookService {private BookDao bookDao;@Overridepublic void save() {System.out.println("book service save...");bookDao.save();}public void setBookDao(BookDao bookDao) {this.bookDao = bookDao;}}

接下来,在 resources 目录下创建 spring 配置文件 applicationContext.xml,并完成 bean 的配置。

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="bookDao" class="com.example.BookDaoImpl"/><bean id="bookService" class="com.example.BookServiceImpl"><property name="bookDao" ref="bookDao"/></bean></beans>

最后,在主程序中获取 IOC 容器,并从容器中获取 bean 对象进行方法调用。

import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class App {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");BookService bookService = (BookService) context.getBean("bookService");bookService.save();}}

通过以上步骤,我们将 BookServiceImpl 和 BookDaoImpl 交给了 Spring 管理,并通过 IOC 容器成功获取了对象,实现了依赖注入。在这个过程中,业务逻辑层不再直接依赖于数据访问层的具体实现,而是通过 Spring 的依赖注入机制获取所需的依赖对象,大大降低了代码的耦合度。

(二)复杂项目中的应用

在更复杂的项目场景中,IoC 和 DI 的重要性和灵活性更加凸显。例如,在一个大型企业级应用中,可能有多个模块,每个模块都有自己的业务逻辑和数据访问需求。如果不使用 IoC 和 DI,各个模块之间的依赖关系会变得非常复杂,难以维护和扩展。

通过 Spring 的 IoC 容器,我们可以将各个模块的对象创建和依赖关系管理集中起来,使得各个模块只需要关注自己的业务逻辑,而不需要关心对象的创建和依赖的获取。例如,在一个电商系统中,订单模块可能依赖于用户模块、商品模块和支付模块。如果没有 IoC 和 DI,订单模块需要自己创建和管理这些依赖模块的对象,这会导致代码复杂且难以维护。而使用 IoC 和 DI 后,订单模块只需要在配置文件中声明自己所需的依赖,Spring 容器会自动创建并注入这些依赖对象。

此外,在复杂项目中,代码的可测试性也是一个重要问题。使用 IoC 和 DI 可以使得代码更容易进行单元测试。因为各个模块的依赖对象可以通过配置文件进行替换,我们可以在测试环境中注入模拟的依赖对象,从而方便地对各个模块进行独立测试。

总之,在复杂项目中,IoC 和 DI 可以帮助我们更好地管理代码的依赖关系,提高代码的可维护性、可扩展性和可测试性,从而降低项目的开发成本和风险。

四、IoC 与 DI 的优势总结

(一)降低代码耦合度

IoC 和 DI 最大的优势之一就是显著降低了代码的耦合度。在传统的 Java 开发中,对象之间往往直接通过硬编码的方式创建依赖关系,这使得代码之间的联系紧密,一旦某个对象发生变化,可能会影响到多个与之相关的对象。而通过 IoC 和 DI,对象的创建和依赖关系的管理由外部容器负责,各个对象之间的依赖通过配置文件或注解来实现,大大减少了对象之间的直接依赖。例如,在一个大型项目中,如果某个模块的实现发生了变化,只需要在配置文件中进行相应的调整,而不需要在所有依赖该模块的地方进行修改,这极大地提高了代码的可维护性。据统计,使用 IoC 和 DI 可以将代码的耦合度降低 50% 以上。

(二)提高可测试性

IoC 和 DI 为代码的测试提供了极大的便利。在单元测试中,我们可以通过配置文件或注解轻松地替换依赖对象为模拟对象,从而隔离被测试对象与外部依赖,使得测试更加专注于被测试对象的内部逻辑。例如,对于一个依赖数据库访问的服务类,在测试时可以使用模拟的数据库访问对象来代替真实的数据库访问对象,这样就可以避免对真实数据库的依赖,提高测试的效率和可靠性。同时,由于依赖关系的明确性,测试用例的编写也更加容易,能够更好地覆盖各种边界情况。

(三)支持面向切面编程

IoC 和 DI 与面向切面编程(AOP)紧密结合,为 Java 开发提供了更强大的功能。通过 AOP,可以将横切关注点(如日志记录、事务管理、安全控制等)从业务逻辑中分离出来,实现代码的模块化和可维护性。而 IoC 和 DI 为 AOP 提供了基础,使得切面可以方便地注入到目标对象中,在不修改业务逻辑代码的情况下实现额外的功能。例如,在一个 Web 应用中,可以通过 AOP 实现统一的日志记录和权限控制,而不需要在每个业务方法中重复编写这些代码。

总之,IoC 和 DI 为 Java 开发带来了诸多优势,不仅提高了代码的质量和可维护性,还为开发高效、可扩展的应用程序提供了有力的支持。在实际开发中,合理运用 IoC 和 DI 可以极大地提高开发效率,降低项目的维护成本。

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

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

相关文章

【C++】set,map,multiset,multimap的介绍和使用

set、map、multiset、multimap set、multiset的介绍和使用1、关联式容器2、键值对3、树形结构的关联式容器4、setset的介绍set的定义set的使用 5、multisetmultiset的介绍multiset的使用 map、multimap的介绍和使用1、map的介绍map的定义insert插入函数map的迭代器find查找函数…

Midjourney基础命令和提示词

1 基础命令 1.1 /imagine prompt 生成图片的核心命令&#xff0c;prompt 后输入描述。 /imagine prompt: A majestic dragon flying over a misty mountain, cinematic lighting, 4K resolution 高级提示 1.1.1 基本参数 图片比例 --ar 图片比例 混乱 Aspect Ratios --…

【代码pycharm】动手学深度学习v2-04 数据操作 + 数据预处理

数据操作 数据预处理 1.数据操作运行结果 2.数据预处理实现运行结果 第四课链接 1.数据操作 import torch # 张量的创建 x1 torch.arange(12) print(1.有12个元素的张量&#xff1a;\n,x1) print(2.张量的形状&#xff1a;\n,x1.shape) print(3.张量中元素的总数&#xff1…

智云-一个抓取web流量的轻量级蜜罐v1.5

智云-一个抓取web流量的轻量级蜜罐v1.5 github地址 https://github.com/xiaoxiaoranxxx/POT-ZHIYUN 新增功能-自定义漏洞信息 可通过正则来添加相关路由以及响应来伪造 nacos的版本响应如下 日流量态势 月流量态势 抓取流量效果

【Android原生问题分析】夸克、抖音划动无响应问题【Android14】

1 问题描述 偶现问题&#xff0c;用户打开夸克、抖音后&#xff0c;在界面上划动无响应&#xff0c;但是没有ANR。回到Launcher后再次打开夸克/抖音&#xff0c;发现App的界面发生了变化&#xff0c;但是仍然是划不动的。 2 log初分析 复现问题附近的log为&#xff1a; 用户…

2024年11月16日 星期六 重新整理Go技术

今日格言 坚持每天进步一点点~ 一个人也可以是一个团队~ 学习全栈开发, 做自己喜欢的产品~~ 简介 大家好, 我是张大鹏, 今天是2024年11月16日星期六, 很高兴在这里给大家分享技术. 今天又是休息的一天, 做了很多的思考, 整理了自己掌握的技术, 比如Java, Python, Golang,…

go-zero(一) 介绍和使用

go-zero 介绍和使用 一、什么是 go-zero&#xff1f; go-zero 是一个基于 Go 语言的微服务框架&#xff0c;提供了高效、简单并易于扩展的 API 设计和开发模式。它主要目的是为开发者提供一种简单的方式来构建和管理云原生应用。 1.go-zero 的核心特性 高性能&#xff1a; g…

VUE+SPRINGBOOT实现邮箱注册、重置密码、登录功能

随着互联网的发展&#xff0c;网站用户的管理、触达、消息通知成为一个网站设计是否合理的重要标志。目前主流互联网公司都支持手机验证码注册、登录。但是手机短信作为服务端网站是需要付出运营商通信成本的&#xff0c;而邮箱的注册、登录、重置密码&#xff0c;无疑成为了这…

多目标优化算法:多目标蛇鹫优化算法(MOSBOA)求解ZDT1、ZDT2、ZDT3、ZDT4、ZDT6,提供完整MATLAB代码

一、蛇鹫优化算法 蛇鹫优化算法&#xff08;Secretary Bird Optimization Algorithm&#xff0c;简称SBOA&#xff09;由Youfa Fu等人于2024年4月发表在《Artificial Intelligence Review》期刊上的一种新型的元启发式算法。该算法旨在解决复杂工程优化问题&#xff0c;特别是…

2024-11-15 Java开发工程师 内推

Java开发工程师 坐标&#xff1a;大连 岗位要求&#xff1a; 1、本科以上学历&#xff0c;计算机相关专业 2、22/23/24届毕业生 小结&#xff1a;有意向的私信发简历

Python绘制雪花

文章目录 系列目录写在前面技术需求完整代码代码分析1. 代码初始化部分分析2. 雪花绘制核心逻辑分析3. 窗口保持部分分析4. 美学与几何特点总结 写在后面 系列目录 序号直达链接爱心系列1Python制作一个无法拒绝的表白界面2Python满屏飘字表白代码3Python无限弹窗满屏表白代码4…

当 docker-compose.yaml 文件部署时,Dify 线上版本升级过程

如果线上 Dify 是通过 docker-compose.yaml 文件部署的&#xff0c;那么当 Dify 版本升级时该如何操作呢&#xff1f;官方已经给出了 Docker compose 和 Source Code 两种方式。相对而言&#xff0c;前者更简单些&#xff0c;至少不需要安装依赖包和迁移数据库文件。为了更加具…

RHCSA学习超详细知识点2命令篇

输入命令行的语法 终端中执行命令需要遵照一定的语法&#xff0c;输入命令的格式如下&#xff1a; 命令 参数命令 -选项 参数 输入命令时可以包含多个选项&#xff0c;假如一个命令有-a,-b,-c,-d四个选项&#xff0c;可以写作 命令 -a -b -c -d 参数 这里的多个选项可以“提…

小米路由器用外网域名访问管理界面

本文在Redmi AX3000 (RA81)设置&#xff0c;其他型号路由器的管理界面端口可能各不相同。 开始之前需要保证路由器SSH功能正常&#xff0c;如果没有SSH可以参考这里。 1. 给WAN口开放80端口 可以通过下载mixbox的firewall插件或者其他防火墙插件开放端口。 2. 把域名解析到路…

✅DAY27贪心算法 | 455.分发饼干 | 376. 摆动序列 | 53. 最大子序和

一、贪心算法 核心理念是每一步都做出局部最优选择&#xff0c;以期最终得到全局最优解。它通常用于求解一些最优化问题&#xff0c;例如最小生成树、最短路径、背包问题等。 二、贪心算法的步骤 1. 定义选择标准&#xff1a;确定每一步如何选择当前最优解。 2. 验证贪心策…

无人机+无人车+机器狗:城市巷战突破技术详解

在城市巷战中&#xff0c;无人机、无人车和机器狗的组合可以形成一种全新的战场突破技术&#xff0c;这种组合能够大幅提升作战效率&#xff0c;减少人员伤亡。以下是对这一技术的详细解析&#xff1a; 一、无人机的作用 1.空中侦察&#xff1a;无人机能够提供高空视角&#x…

智慧环保之环境网格化监测解决方案

1. 引言 智慧环保网格化监测解决方案&#xff0c;旨在通过先进的信息技术手段&#xff0c;实现环境质量的全面、精准、实时监测&#xff0c;为环境治理提供科学依据和决策支持。 2. 背景与挑战 当前&#xff0c;环境污染问题日益严峻&#xff0c;治理难度不断加大。传统监测手…

vue2+3 —— Day5/6

自定义指令 自定义指令 需求&#xff1a;当页面加载时&#xff0c;让元素获取焦点&#xff08;一进页面&#xff0c;输入框就获取焦点&#xff09; 常规操作&#xff1a;操作dom “dom元素.focus()” 获取dom元素还要用ref 和 $refs <input ref"inp" type&quo…

2024 RISC-V 中国峰会 演讲幻灯片和视频回放 均已公开

目录 一、幻灯片地址&#xff1a; 二、演讲视频&#xff1a; 一、幻灯片地址&#xff1a; RVSC2024/slides at main cnrv/RVSC2024 GitHub 二、演讲视频&#xff1a; RISC-V国际基金会的个人空间-RISC-V国际基金会个人主页-哔哩哔哩视频 参考&#xff1a; 1、RISC-V 中…

sudu问题 --> 添加用户到系统配置文件里面

我们重新创建一个用户lisi 密码&#xff1a;lisixxxxxxxx 重新连接&#xff0c;输入用户&#xff0c;输入密码 是不行的 我们输入exit退出 打开文件 再切换身份&#xff0c;切换为超级管理员root 输入密码 再打开 我们打开后设置行号在50行左右可以看到我们的目标 我们再进行…