《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.rhkb.cn/news/473833.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vulnhub靶场案例渗透[9]- HackableIII

文章目录 一、靶场搭建1. 靶场描述2. 下载靶机环境3. 靶场搭建 二、渗透靶场1. 确定靶机IP2. 探测靶场开放端口及对应服务3. 扫描网络目录结构4. 敏感数据获取5. 获取shell6. 提权6.1 敏感信息获取6.2 lxd提权 一、靶场搭建 1. 靶场描述 Focus on general concepts about CTF…

抖音热门素材去哪找?优质抖音视频素材网站推荐!

是不是和我一样&#xff0c;刷抖音刷到停不下来&#xff1f;越来越多的朋友希望在抖音上创作出爆款视频&#xff0c;但苦于没有好素材。今天就来推荐几个超级实用的抖音视频素材网站&#xff0c;让你的视频内容立刻变得高大上&#xff01;这篇满是干货&#xff0c;直接上重点&a…

如何轻松导出所有 WordPress URL 为纯文本格式

作为一名多年的 WordPress 使用者&#xff0c;我深知管理一个网站的复杂性。从迁移网站、设置重定向到整理内容结构&#xff0c;每一步都需要精细处理。而拥有所有 URL 的清单&#xff0c;不仅能让这些工作变得更加简单&#xff0c;还能为后续的管理提供极大的便利。其实&#…

vue项目使用eslint+prettier管理项目格式化

代码格式化、规范化说明 使用eslintprettier进行格式化&#xff0c;vscode中需要安装插件ESLint、Prettier - Code formatter&#xff0c;且格式化程序选择为后者&#xff08;vue文件、js文件要分别设置&#xff09; 对于eslint规则&#xff0c;在格式化时不会全部自动调整&…

Ubuntu 18.04 配置sources.list源文件(无法安全地用该源进行更新,所以默认禁用该源)

如果你 sudo apt update 时出现诸如 无法安全地用该源进行更新&#xff0c;所以默认禁用该源 的错误&#xff0c;那就换换源吧&#xff0c;链接&#xff1a; https://mirror.tuna.tsinghua.edu.cn/help/ubuntu/ 注意版本&#xff1a; 修改源文件&#xff1a; sudo nano /etc…

5. langgraph中的react agent使用 (从零构建一个react agent)

1. 定义 Agent 状态 首先&#xff0c;我们需要定义 Agent 的状态&#xff0c;这包括 Agent 所持有的消息。 from typing import (Annotated,Sequence,TypedDict, ) from langchain_core.messages import BaseMessage from langgraph.graph.message import add_messagesclass …

【网络】什么是交换机?switch

交换机&#xff08;Switch&#xff09;意为“开关”&#xff0c;是一种用于电&#xff08;光&#xff09;信号转发的网络设备。以下是关于交换机的详细解释&#xff1a; 一、交换机的基本定义 功能&#xff1a;交换机能为接入交换机的任意两个网络节点提供独享的电信号通路&am…

【AlphaFold3】开源本地的安装及使用

文章目录 安装安装DockerInstalling Docker on Host启用Rootless Docker 安装 GPU 支持安装 NVIDIA 驱动程序安装 NVIDIA 对 Docker 的支持 获取 AlphaFold 3 源代码获取基因数据库获取模型参数构建将运行 AlphaFold 3 的 Docker 容器 参考 AlphaFold3: https://github.com/goo…

【免越狱】iOS砸壳 可下载AppStore任意版本 旧版本IPA下载

软件介绍 下载iOS旧版应用&#xff0c;简化繁琐的抓包流程。 一键生成去更新IPA&#xff08;手机安装后&#xff0c;去除App Store的更新检测&#xff09;。 软件界面 支持系统 Windows 10/Windows 8/Windows 7&#xff08;由于使用了Fiddler库&#xff0c;因此需要.Net环境…

shell 100例

1、每天写一个文件 (题目要求&#xff09; 请按照这样的日期格式(xxxx-xx-xx每日生成一个文件 例如生成的文件为2017-12-20.log&#xff0c;并且把磁盘的使用情况写到到这个文件中不用考虑cron&#xff0c;仅仅写脚本即可 [核心要点] date命令用法 df命令 知识补充&#xff1…

Acrobat Pro DC 2023(pdf免费转化word)

所在位置 通过网盘分享的文件&#xff1a;Acrobat Pro DC 2023(64bit).tar 链接: https://pan.baidu.com/s/1_m8TT1rHTtp5YnU8F0QGXQ 提取码: 1234 --来自百度网盘超级会员v4的分享 安装流程 打开安装所在位置 进入安装程序 找到安装程序 进入后点击自定义安装&#xff0c;这里…

linux之调度管理(5)-实时调度器

一、概述 在Linux内核中&#xff0c;实时进程总是比普通进程的优先级要高&#xff0c;实时进程的调度是由Real Time Scheduler(RT调度器)来管理&#xff0c;而普通进程由CFS调度器来管理。 实时进程支持的调度策略为&#xff1a;SCHED_FIFO和SCHED_RR。 SCHED_FIFO&#xff…

在arm64架构下, Ubuntu 18.04.5 LTS 用命令安装和卸载qt4、qt5

问题&#xff1a;需要在 arm64下安装Qt&#xff0c;QT源码编译失败以后&#xff0c;选择在线安装&#xff01; 最后安装的版本是Qt5.9.5 和QtCreator 4.5.2 。 一、ubuntu安装qt4的命令(亲测有效)&#xff1a; sudo add-apt-repository ppa:rock-core/qt4 sudo apt updat…

Qt 之 qwt和QCustomplot对比

QWT&#xff08;Qt Widgets for Technical Applications&#xff09;和 QCustomPlot 都是用于在 Qt 应用程序中绘制图形和图表的第三方库。它们各有优缺点&#xff0c;适用于不同的场景。 以下是 QWT 和 QCustomPlot 的对比分析&#xff1a; 1. 功能丰富度 QWT 功能丰富&a…

实用教程:如何无损修改MP4视频时长

如何在UltraEdit中搜索MP4文件中的“mvhd”关键字 引言 在视频编辑和分析领域&#xff0c;有时我们需要深入到视频文件的底层结构中去。UltraEdit&#xff08;UE&#xff09;和UEStudio作为强大的文本编辑器&#xff0c;允许我们以十六进制模式打开和搜索MP4文件。本文将指导…

使用nossl模式连接MySQL数据库详解

使用nossl模式连接MySQL数据库详解 摘要一、引言二、nossl模式概述2.1 SSL与nossl模式的区别2.2 选择nossl模式的场景三、在nossl模式下连接MySQL数据库3.1 准备工作3.2 C++代码示例3.3 代码详解3.3.1 初始化MySQL连接对象3.3.2 连接到MySQL数据库3.3.3 执行查询操作3.3.4 处理…

Linux下编译MFEM

本文记录在Linux下编译MFEM的过程。 零、环境 操作系统Ubuntu 22.04.4 LTSVS Code1.92.1Git2.34.1GCC11.4.0CMake3.22.1Boost1.74.0oneAPI2024.2.1 一、安装依赖 二、编译代码 附录I: CMakeUserPresets.json {"version": 4,"configurePresets": [{&quo…

号卡分销系统,号卡系统,物联网卡系统源码安装教程

号卡分销系统&#xff0c;号卡系统&#xff0c;物联网卡系统&#xff0c;&#xff0c;实现的高性能(PHP协程、PHP微服务)、高灵活性、前后端分离(后台)&#xff0c;PHP 持久化框架&#xff0c;助力管理系统敏捷开发&#xff0c;长期持续更新中。 主要特性 基于Auth验证的权限…

Java基础-集合

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 前言 一、Java集合框架概述 二、Collection接口及其实现 2.1 Collection接口 2.2 List接口及其实现 …

基于Python的仓库管理系统设计与实现

背景&#xff1a; 基于Python的仓库管理系统功能介绍 本仓库管理系统采用Python语言开发&#xff0c;利用Django框架和MySQL数据库&#xff0c;实现了高效、便捷的仓库管理功能。 用户管理&#xff1a; 支持员工和管理员角色的管理。 用户注册、登录和权限分配功能&#x…