Spring Boot 核心知识点:依赖注入 (Dependency Injection)

Spring Boot 核心知识点:依赖注入 (Dependency Injection)

一、引言

在软件开发中,对象之间的依赖关系是不可避免的。一个对象通常需要与其他对象协作才能完成其功能。传统的对象创建方式往往需要在对象内部显式地创建或查找其依赖的对象,这会导致代码的耦合性高、可测试性差、难以维护和扩展。

Spring 框架的核心思想之一就是依赖注入(Dependency Injection,简称 DI)。Spring Boot 作为构建在 Spring 框架之上的快速开发框架,自然也继承并广泛使用了依赖注入。本文将深入探讨 Spring Boot 中的依赖注入机制,包括其概念、优势、实现方式以及在实际开发中的应用。

二、什么是依赖注入 (Dependency Injection)?

依赖注入是一种设计模式,其核心思想是将对象所依赖的其他对象(即“依赖”)的创建和管理工作交给外部容器(在 Spring Boot 中是 Spring IoC 容器),而不是由对象自身来负责。当对象需要使用其依赖时,容器会将所需的依赖“注入”到对象中。

简单理解:

想象一下你想要组装一台电脑。CPU、内存、硬盘等都是电脑的组件(相当于对象),它们之间存在依赖关系(例如,主板需要 CPU 才能工作)。

  • 传统方式: 你可能需要自己去市场上购买每个组件,并手动将它们安装到主板上。这个过程需要你了解每个组件的规格和兼容性。
  • 依赖注入: 你将主板交给一个专业的电脑组装人员(相当于 Spring IoC 容器),并告诉他你需要哪些组件。组装人员会负责购买合适的组件并将它们正确地安装到主板上。你只需要拿到组装好的电脑即可,无需关心组件是如何创建和组装的。

在软件开发中,“组装人员”就是 Spring IoC 容器,“组件”就是 Spring 管理的 Bean,“安装”的过程就是依赖注入。

三、依赖注入的优势

采用依赖注入能够带来诸多好处:

  1. 降低耦合性 (Decoupling):对象不再需要知道如何创建和管理其依赖对象,它们只关心如何使用依赖。这降低了对象之间的耦合度,使得代码更加灵活和易于修改。
  2. 提高可测试性 (Improved Testability):由于依赖关系由外部注入,因此在进行单元测试时,可以轻松地使用 Mock 对象或 Stub 对象来替代真实的依赖,从而隔离被测试的代码,提高测试的效率和准确性。
  3. 提高可维护性 (Enhanced Maintainability):当依赖关系发生变化时,只需要修改配置(例如 Spring 的配置文件或注解),而不需要修改使用依赖的对象的代码。这使得系统更容易维护和演进。
  4. 提高可重用性 (Increased Reusability):解耦的对象可以更容易地在不同的场景中被重用,因为它们不依赖于特定的依赖创建方式。
  5. 更好的配置管理 (Better Configuration Management):依赖关系的管理集中在 IoC 容器中,使得配置更加清晰和易于管理。

四、Spring Boot 中依赖注入的实现方式

Spring Boot 继承了 Spring 框架的依赖注入机制,主要通过以下几种方式实现:

  1. 构造器注入 (Constructor Injection)

    • 通过在类的构造函数上使用 @Autowired 注解(在 Spring 4.3 及之后,如果类只有一个构造函数,@Autowired 可以省略),Spring IoC 容器会自动解析构造函数所需的依赖并将其注入。
    • 优点:
      • 强制依赖:能够确保在创建对象时所需的依赖已经准备好,有助于提高代码的健壮性。
      • 不可变性:注入的依赖通常是 final 的,保证了对象的不可变性。
      • 更易于测试:所有必需的依赖都在构造函数中声明,使得创建测试对象更加方便。
    • 示例:

    ``java
    @Service
    public class UserService {

    private final UserRepository userRepository;
    private final EmailService emailService;@Autowired // 在 Spring 4.3+ 可以省略
    public UserService(UserRepository userRepository, EmailService emailService) {this.userRepository = userRepository;this.emailService = emailService;
    }// ... 其他业务逻辑
    

    }
    ``

  2. Setter 方法注入 (Setter Injection)

    • 通过在类的 Setter 方法上使用 @Autowired 注解,Spring IoC 容器会在对象创建后调用这些 Setter 方法,并将所需的依赖注入。
    • 优点:
      • 可选依赖:允许某些依赖是可选的,如果没有找到对应的 Bean,Setter 方法不会被调用。
      • 灵活性:可以在对象创建后动态地设置依赖。
    • 缺点:
      • 依赖可能在使用前未被注入,需要进行空指针检查。
      • 对象的可变性可能增加。
    • 示例:

    ``java
    @Service
    public class ProductService {

    private ProductRepository productRepository;
    private Logger logger;@Autowired
    public void setProductRepository(ProductRepository productRepository) {this.productRepository = productRepository;
    }@Autowired(required = false) // 表示 logger 是可选的依赖
    public void setLogger(Logger logger) {this.logger = logger;
    }// ... 其他业务逻辑
    

    }
    ``

  3. 字段注入 (Field Injection)

    • 通过在类的字段上直接使用 @Autowired 注解,Spring IoC 容器会直接将所需的依赖注入到字段中。
    • 优点:
      • 代码简洁。
    • 缺点:
      • 难以进行单元测试:由于依赖是在类内部通过反射注入的,很难在测试时替换依赖。
      • 隐藏了依赖关系:不容易看出对象所依赖的组件。
      • 可能导致循环依赖问题。
    • 示例:

    ``java
    @Service
    public class OrderService {

    @Autowired
    private OrderRepository orderRepository;@Autowired
    private InventoryService inventoryService;// ... 其他业务逻辑
    

    }
    ``

    注意:虽然字段注入在代码上看起来很简洁,但通常不推荐在生产代码中使用,因为它会降低代码的可测试性和可维护性。构造器注入是推荐的首选方式。

五、@Autowired 注解详解

@Autowired 是 Spring 中用于实现依赖注入的核心注解。它可以应用在构造函数、Setter 方法和字段上。

  • required 属性 (默认 true)

    • @Autowired 应用于构造函数或 Setter 方法时,如果 Spring IoC 容器中找不到匹配的依赖 Bean,默认情况下会抛出 NoSuchBeanDefinitionException
    • 可以将 required 属性设置为 false,表示该依赖是可选的,如果找不到匹配的 Bean,Spring 不会报错,依赖会保持为 null
  • 处理多个匹配的 Bean (@Primary@Qualifier)

    • 如果 Spring IoC 容器中存在多个类型相同的 Bean,Spring 无法确定要注入哪一个,会抛出 NoUniqueBeanDefinitionException
    • 可以使用以下两种方式解决:
      • @Primary 注解: 在一个候选的 Bean 上使用 @Primary 注解,将其标记为首选的 Bean。当需要注入该类型的依赖时,如果没有明确指定,Spring 会优先选择带有 @Primary 注解的 Bean。
      • @Qualifier 注解: 使用 @Qualifier 注解指定要注入的 Bean 的名称。@Qualifier 可以与 @Autowired 一起使用,通过名称来精确匹配需要注入的 Bean。

    示例(处理多个匹配的 Bean):

    ``java
    @Component(“fileLogger”)
    public class FileLogger implements Logger {
    // … 实现
    }

    @Primary
    @Component(“consoleLogger”)
    public class ConsoleLogger implements Logger {
    // … 实现
    }

    @Service
    public class LogService {

    private final Logger logger;@Autowired
    public LogService(Logger logger) { // 将注入 ConsoleLogger (因为它是 @Primary)this.logger = logger;
    }// ...
    

    }

    @Service
    public class AnotherService {

    private final Logger fileLogger;@Autowired
    public AnotherService(@Qualifier("fileLogger") Logger fileLogger) { // 明确指定注入名为 "fileLogger" 的 Beanthis.fileLogger = fileLogger;
    }// ...
    

    }
    ``

六、Spring Boot 中的自动配置与依赖注入

Spring Boot 的自动配置机制与依赖注入紧密相关。当 Spring Boot 启动时,它会根据 classpath 下的依赖和应用的配置信息,自动创建并管理许多 Bean,这些 Bean 随后可以通过依赖注入的方式在你的组件中使用。

例如,当你引入 spring-boot-starter-data-jpa 依赖时,Spring Boot 会自动配置 EntityManagerFactoryDataSourceTransactionManager 等 Bean。你只需要在你的 Repository 或 Service 中使用 @Autowired 注解注入这些 Bean 即可。

七、总结

依赖注入是 Spring 框架和 Spring Boot 的核心特性之一,它通过将对象的依赖关系交给 Spring IoC 容器管理,实现了对象之间的解耦,提高了代码的可测试性、可维护性和可扩展性。在 Spring Boot 开发中,我们通常使用 @Autowired 注解结合构造器注入、Setter 方法注入或字段注入来实现依赖注入。推荐优先使用构造器注入,因为它能够提供更好的代码健壮性和可测试性。理解和熟练掌握依赖注入是成为一名合格的 Spring Boot 开发者的关键。

八、参考资料

  • Spring Framework Documentation
  • Spring Boot Reference Documentation

希望这篇文章能够帮助你深入理解 Spring Boot 中的依赖注入机制。如果你有任何其他问题,欢迎继续提问。

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

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

相关文章

c中<string.h>

常见错误与最佳实践 缓冲区溢出: strcpy 和 strcat 不检查目标缓冲区大小,需手动确保空间足够。替代方案:使用 strncpy 和 strncat,或动态分配内存(如 malloc)。 未终止的字符串: 确保字符串以…

C++动态规划从入门到精通

一、动态规划基础概念详解 什么是动态规划 动态规划(Dynamic Programming,DP)是一种通过将复杂问题分解为重叠子问题,并存储子问题解以避免重复计算的优化算法。它适用于具有以下两个关键性质的问题: 最优子结构&…

TypeScript + Vue:类风格组件如何引领前端新潮流?

🚀 TypeScript Vue:用类风格组件打造“假货比对”神器!🌟 2025 年,前端开发早已进入“类型安全 模块化”的黄金时代。TypeScript (TS) 的类风格组件正在席卷 Vue 社区,为开发者带来更优雅、更强大的编码体…

Odoo 18 中的列表(list) 、表单(Form)、数据透视表、图表视图、看板视图、活动视图、日历视图等综合应用实例

Odoo 18 中的 视图应用实例 在 Odoo 中,视图是用户界面中表示业务对象的重要组成部分。无论您是扩展现有功能还是创建全新的功能,业务对象都至关重要。这些对象通过不同类型的视图向用户展示,而 Odoo 会根据 XML 描述动态生成这些视图。 列…

【Linux】Bash是什么?怎么使用?

李升伟 整理 什么是 Bash? Bash(Bourne Again Shell)是一种 命令行解释器(Shell),广泛用于 Unix 和 Linux 操作系统。它是 Bourne Shell(sh) 的增强版,提供了更多的功能…

Golang开发

Golang 文章目录 Golang预备技术一、算法与数据结构第1章:基础算法第2章:数据结构第3章:搜索与图论第4章:数论第5章:动态规划第6章:贪心第7章:算法竞赛入门 二、Linux操作系统与Shell编程三、计…

AI +低代码平台实现个性化用户体验设计

目录 一、引言 二、低代码平台与用户体验现状 (一)低代码平台的普及与应用 (二)传统低代码平台用户体验的局限性 三、AI 在个性化用户体验设计中的关键作用 (一)用户行为分析与洞察 (二&a…

synchronized与 Java内置锁(未写完)

文章目录 一、 synchronized 关键字二、Java对象结构1. 对象头2. 对象体3. 对齐字节4. 对象头中的字段长度5. Mark Word 的结构信息6. 使用 JOL 工具查看对象的布局 三、Java 内置锁机制演进过程1. 无锁状态2. 偏向锁状态3. 轻量级锁状态4. 重量级锁状态 一、 synchronized 关键…

【MySQL数据库】多表查询(笛卡尔积现象,联合查询、内连接、左外连接、右外连接、子查询)-通过练习快速掌握法

在DQL的基础查询中,我们已经学过了多表查询的一种:联合查询(union)。本文我们将系统的讲解多表查询。 笛卡尔积现象 首先,我们想要查询emp表和stu表两个表,按照我们之前的知识栈,我们直接使用…

网易云信架构升级实践,故障恢复时间缩至8秒

一、项目背景 网易云信是网易旗下集IM与音视频技术于一体的PaaS服务平台,为全球提供融合通信与视频的核心功能和组件,包括IM即时通讯、短信、信令等通信服务,以及RTC、直播、点播、互动直播、互动白板等音视频服务,此外&#xf…

[HelloCTF]PHPinclude-labs超详细WP-Level 1-FILE协议

源码分析 <?php include("get_flag.php");isset($_GET[wrappers]) ? include("file://".$_GET[wrappers]) : ;highlight_file(__FILE__); ?>第一句 include("get_flag.php");, 使代码包含了 get_flag.php 的内容 大概是生成 Flag 之类的…

MongoDB 可观测性最佳实践

MongoDB 介绍 MongoDB 是一个高性能、开源的 NoSQL 数据库&#xff0c;它采用灵活的文档数据模型&#xff0c;非常适合处理大规模的分布式数据。MongoDB 的文档存储方式使得数据结构可以随需求变化而变化&#xff0c;提供了极高的灵活性。它支持丰富的查询语言&#xff0c;允许…

4.angular 服务

服务是在controller里面引入的服务&#xff1a; 最好是内部服务在前面&#xff0c;自定义服务在后面 内部服务 $scope $scope.$watch(‘属性名’, function(newVal, oldVal) {}, true) true是深度监听,对象函数等$scope.$apply 触发页面更新,里面传入回调函数,比如说之前那个…

HarmonyOS NEXT开发进阶(十二):build-profile.json5 文件解析

文章目录 一、前言二、Hvigor脚本文件三、任务与任务依赖图四、多模块管理4.1 静态配置模块 五、分模块编译六、配置多目标产物七、配置APP多目标构建产物八、定义 product 中包含的 target九、拓展阅读 一、前言 编译构建工具DevEco Hvigor&#xff08;以下简称Hvigor&#x…

【强化学习基石】Deepseek V3技术报告中的GRPO算法是什么?

1. Deepseek V3技术报告中的GRPO算法是什么? GRPO(Generalized Relative Policy Optimization)是一种在强化学习领域用于策略优化的算法。它主要是在策略梯度方法的基础上进行改进,目的是更有效地优化策略网络,从而提高智能体在环境中的表现。 GRPO 的核心思想是通过相对…

VSCode C/C++ 开发环境完整配置及常见问题(自用)

这里主要记录了一些与配置相关的内容。由于网上教程众多&#xff0c;部分解决方法并不能完全契合我遇到的问题&#xff0c;因此我选择以自己偏好的方式&#xff0c;对 VSCode 进行完整的配置&#xff0c;并记录在使用过程中遇到的问题及解决方案。后续内容也会持续更新和完善。…

Billu_b0x靶机攻略

1&#xff0c;安装好靶机并打开&#xff0c;打开kali进行扫描得到靶机ip为192.168.50.138 2&#xff0c;访问靶机以及扫描出的目录 3&#xff0c;访问test.php发现file参数为空&#xff0c;尝试拼接其他路径来访问&#xff0c;发现可以file传参&#xff0c;利用插件进行post传参…

如何搭建一个安全经济适用的TRS交易平台?

TRS&#xff08;总收益互换&#xff09;一种多方参与的投资方式&#xff0c;也是绝对收益互换&#xff08;total return swap&#xff09;的一种形式。 它是一种衍生合约&#xff0c;是一种金融衍生品的合约&#xff0c;是指交易双方在协议期间将参照资产的总收益转移给信用保…

LeetCode 解题思路 16(Hot 100)

解题思路&#xff1a; 初始化辅助节点&#xff1a; dummy&#xff1a;哑节点。pre&#xff1a;当前链表的前一个节点。start&#xff1a;当前链表的第一个节点。end&#xff1a;当前链表的最后一个节点。nextStart&#xff1a;end.next&#xff0c;下组链表的第一个节点&…

数据结构——串、数组和广义表

串、数组和广义表 1. 串 1.1 串的定义 串(string)是由零个或多个字符组成的有限序列。一般记为 S a 1 a 2 . . . a n ( n ≥ 0 ) Sa_1a_2...a_n(n\geq0) Sa1​a2​...an​(n≥0) 其中&#xff0c;S是串名&#xff0c;单引号括起来的字符序列是串的值&#xff0c; a i a_i a…