”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > Spring Boot 中的依赖注入:幕后向导

Spring Boot 中的依赖注入:幕后向导

发布于2024-11-09
浏览:282

Dependency Injection in Spring Boot: The Wizard Behind the Curtain

Spring Boot 中的依赖注入:幕后向导

是否曾经感觉 Spring Boot 是一个神奇的管家,不知怎的,它只是 知道 你需要什么,然后把它放在银盘上送给你?这基本上就是依赖注入 (DI)。您可能已经使用过 DI 一百次,却没有停下来思考:Spring 到底是如何知道注入什么以及何时注入?

如果这听起来像您,欢迎加入!我们将进行一次有趣的幕后之旅,了解 Spring Boot 的 DI 如何发挥其魔力,从它如何管理 bean、@Autowired 和 bean 生命周期(从诞生到销毁)开始。在本博客结束时,您将像专业人士一样展示您新发现的 DI 知识。


什么是依赖注入?为什么你应该关心?

通俗地说,依赖注入就像是让杂货送货上门,而不是自己出去买。它是将“注入”依赖项(bean)的责任委托给 Spring,这样您就不必手动创建对象或担心它们的生命周期。

假设您是一名厨师,经营着一个繁忙的厨房(您的应用程序)。你没有时间每次需要鸡蛋、牛奶和糖时都跑出去买。如果有人(比如 Spring)能够神奇地在您需要的时候准确地交付您需要的一切,那不是很棒吗?

这正是 Spring DI 所做的:它找到您需要的所有成分(bean)并将它们注入到您的代码中,而无需您费力。很整洁吧?


Spring Container 的魔力:您的私人管家

好吧,这就是奇迹发生的地方。当您使用 SpringApplication.run() 运行 Spring Boot 应用程序时,Spring 会引导 ApplicationContext - 将其视为您的管家的说明手册。它确切地知道要获取什么以及何时获取。

我们一步步分解:

  1. 容器初始化: 当你点击 SpringApplication.run() 时,Spring 容器(又名 ApplicationContext)就会启动。这就像打开虚拟餐厅的大门,一切准备就绪。

  2. Bean 创建: 容器会扫描您的代码以查找 @Component、@Service、@Repository 或 @Controller 等注释。其中每一个都成为一个 bean——一个由 Spring 管理的对象。将它们视为厨房中的基本成分:面粉、糖、鸡蛋等。

  3. BeanFactory 来救援: Spring Boot 使用 BeanFactory 来创建和管理这些 bean。该工厂确切地知道如何以及何时创建您的 Bean,确保它们在需要时可用。

  4. 依赖注入:一旦bean准备好了,Spring就会在你用@Autowired标记的地方注入它们。这就像咖啡师不仅会煮咖啡,还会将咖啡送到需要的地方。你甚至不需要考虑它——一切都会出现


@Autowired 如何工作?豆子大侦探福尔摩斯

啊,很好的 @Autowired 注释。有没有想过 Spring 如何神奇地知道在哪里注入依赖项?它有点像一个侦探,将您的需求与注册表中正确的 bean 相匹配。

其工作原理如下:

  • 类型匹配:当Spring看到@Autowired时,它会在容器中寻找相同类型的bean。想象一下,您订购了咖啡豆(一个 CoffeeService 类),Spring 会在其 Bean 存储库中查找并说:“啊,我已经有了这些!让我给你注射。”

  • 限定符: 但是如果您有多个相同类型的 bean 怎么办?在这种情况下,Spring 可能会崩溃并抛出“NoUniqueBeanDefinitionException”之类的异常。但别担心——你可以通过使用 @Qualifier 指定要注入哪个 bean 来让 Spring 平静下来:

@Autowired
@Qualifier("espressoBean")
private CoffeeService coffeeService;
  • 构造函数注入(最好的方法): 如今,构造函数注入是最酷的东西。它不仅使您的 Bean 不可变,而且还使测试变得轻而易举。操作方法如下:
public class CoffeeShop {

    private final CoffeeService coffeeService;

    @Autowired
    public CoffeeShop(CoffeeService coffeeService) {
        this.coffeeService = coffeeService;
    }
}

Spring 继续自动驾驶,将 bean 注入到构造函数中,瞧,一切顺利!


Spring Bean 的生命周期:从出生到退休派对

Spring Boot 中的 Bean 不仅仅是对象。他们拥有充实的生活、完整的起源故事、充实的职业生涯和最终的退休生活。让我们跟踪一下 bean 的生命周期:

  1. 实例化(诞生): 首先,Spring 创建 bean 的一个实例。这就像豆子的诞生一样。春天说:“给你,小家伙!”并将其传递到容器中。

  2. 依赖注入: 创建 bean 后,Spring 会用依赖项填充它(就像蛋糕配方中的成分)。这就是@Autowired 发挥作用的地方。您的 Bean 获得了正常工作所需的一切。

  3. 初始化后: 如果你有用@PostConstruct注释的方法,Spring会在注入依赖项后调用这些方法。这就像在豆子开始工作之前给它涂上一层新油漆。

  4. 准备行动: 现在你的豆子已经活过来了。它已经准备好迎接世界了!

  5. 预销毁(退休): 当应用程序关闭时,Spring 调用 @PreDestroy 方法让 bean 正常退出。这是 Bean 的退休聚会,它清理其资源。

  6. Bean 销毁: 最后,bean 被销毁。是时候安息了。

以下是如何在代码中跟踪这些生命周期事件:

@Component
public class CoffeeBean {

    @PostConstruct
    public void onStart() {
        System.out.println("Bean is ready to brew some coffee!");
    }

    @PreDestroy
    public void onEnd() {
        System.out.println("Bean is retiring. Goodbye, world!");
    }
}

Bean Scopes:魔法能持续多久?

并非所有豆类的预期寿命都相同。 Spring Boot 允许您为 beans 定义不同的范围——基本上是它们的寿命。最常见的两个是:

  • Singleton(默认): bean 只有一个实例,在整个应用程序中共享。这就像整个咖啡店都拥有一台浓缩咖啡机。

  • 原型: 每次需要时都会创建一个新的 bean 实例。想象一下,每个订单都有一台新鲜的浓缩咖啡机。它占用大量资源,但有时是必要的。

@Component
@Scope("prototype")
public class LatteMachine {
    // This bean is made fresh for every use
}

SpringApplication.run():DI 大师

好吧,让我们来谈谈使用 SpringApplication.run() 运行 Spring Boot 应用程序时会发生什么。这个方法是启动整个 DI 过程的大师。

  1. 启动应用程序上下文: Spring 启动 ApplicationContext,所有 bean 都位于其中。
  2. 扫描 Beans: Spring 扫描您的代码以查找 Bean 并注册它们。
  3. 注入依赖关系:一旦bean准备好,Spring就开始在使用@Autowired的地方注入它们。
  4. 启动应用程序:一切就绪后,应用程序就会上线。魔法完成。

现实生活中的类比:咖啡店里的 DI

将您的 Spring Boot 应用程序视为一家咖啡店。你是主人,豆子就是你的原料:咖啡、牛奶、糖等。你不用自己跑来跑去管理这些原料,而是有一个咖啡师(Spring 容器)来获取所有东西并将其准确地交付到它所在的位置需要。

您所要做的就是下订单(设置您的 @Autowired 字段),咖啡师会处理剩下的事情 — 为您的客户完美地冲泡一杯充满依赖的咖啡(应用程序)。


总结:DI 是你的超能力

归根结底,依赖注入使得 Spring Boot 成为如此强大的框架。它简化了您的生活,管理您的 Bean,并确保您的代码易于维护和扩展。

既然您已经窥视了幕后,您就拥有了许多开发人员认为理所当然的超能力。所以,继续吧——像现在的向导一样开始使用 DI。下次您看到@Autowired 时,您就会确切地知道幕后发生了什么。


希望这篇博客能让您对Spring Boot DI有更深入的了解,并给您留下微笑。现在去注入一些豆子并向你的朋友展示它是如何完成的!


对于一个有趣、信息丰富且易于理解的博客来说怎么样?如果您需要更多调整,请告诉我!

版本声明 本文转载于:https://dev.to/janisyed18/dependency-injection-in-spring-boot-the-wizard-behind-the-curtain-49n8?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • Bootstrap 4 Beta 中的列偏移发生了什么?
    Bootstrap 4 Beta 中的列偏移发生了什么?
    Bootstrap 4 Beta:列偏移的删除和恢复Bootstrap 4 在其 Beta 1 版本中引入了重大更改柱子偏移了。然而,随着 Beta 2 的后续发布,这些变化已经逆转。从 offset-md-* 到 ml-auto在 Bootstrap 4 Beta 1 中, offset-md-*...
    编程 发布于2024-11-16
  • 如何使用 MySQL 查找今天生日的用户?
    如何使用 MySQL 查找今天生日的用户?
    如何使用 MySQL 识别今天生日的用户使用 MySQL 确定今天是否是用户的生日涉及查找生日匹配的所有行今天的日期。这可以通过一个简单的 MySQL 查询来实现,该查询将存储为 UNIX 时间戳的生日与今天的日期进行比较。以下 SQL 查询将获取今天有生日的所有用户: FROM USERS ...
    编程 发布于2024-11-16
  • Vite中环境变量的处理
    Vite中环境变量的处理
    在现代 Web 开发中,管理敏感数据(例如 API 密钥、数据库凭据以及不同环境的各种配置)至关重要。将这些变量直接存储在代码中可能会带来安全风险并使部署复杂化。 Vite,一个现代的前端构建工具,提供了一种通过.env文件管理环境变量的简单方法。 什么是 .env 文件? .env 文件是一个简单...
    编程 发布于2024-11-16
  • 如何使用 Django REST Framework 高效处理嵌套序列化器中的外键分配?
    如何使用 Django REST Framework 高效处理嵌套序列化器中的外键分配?
    Django REST Framework 中的嵌套序列化器的外键分配Django REST Framework (DRF) 提供了一种管理外键关系的便捷方法序列化数据。然而,在嵌套序列化器中获得所需的行为可能具有挑战性。嵌套序列化器中的外键分配嵌套序列化器继承其父序列化器的行为。默认情况下,它们不...
    编程 发布于2024-11-16
  • 如何从 CodeIgniter URL 中删除“index.php”?
    如何从 CodeIgniter URL 中删除“index.php”?
    CodeIgniter .htaccess 和 URL 重写问题导航 CodeIgniter 应用程序通常需要从 URL 中删除“index.php”,以允许用户访问具有更清晰语法的页面。不过,新用户在这个过程中可能会遇到困难。删除“index.php”的关键在于修改应用程序配置文件(applica...
    编程 发布于2024-11-16
  • 您可以在 `` 标签内嵌套更多的 `` 元素吗?
    您可以在 `` 标签内嵌套更多的 `` 元素吗?
    不常见的 HTML 结构: 可以容纳 以外的标签吗?在 HTML 世界中,嵌套标签可以创建复杂的结构。然而,某些标签的放置有时会受到限制。以神秘的 标签为例,许多人认为它只能嵌套 元素。深入研究:您的询问源于探索标签是否有效的愿望除了 之外的其他人都可以在 中找到一个家。为了揭开这个概念的...
    编程 发布于2024-11-16
  • 如何使用 XPath 条件选择 XML 文档中的特定节点?
    如何使用 XPath 条件选择 XML 文档中的特定节点?
    利用 XPath 条件选择节点通过 XPath 导航 XML 文档时,通常需要根据特定条件限制检索的节点。在此示例中,我们的任务是根据日期属性有选择地检索节点。以下 XPath 表达式从提供的 XML 文档中检索所有 节点:$nodes = $xml->xpath('//xml/events...
    编程 发布于2024-11-16
  • 为什么“margin: auto”不能与绝对定位的元素一起使用?
    为什么“margin: auto”不能与绝对定位的元素一起使用?
    了解绝对定位边距自动问题当将“position:absolute”应用于具有“margin-left:auto”和“的元素时margin-right: auto”,您可能会注意到边距似乎没有效果。此行为不同于“位置:相对”,其中边距按预期工作。为了理解这种差异,让我们更深入地研究底层机制。当一个元素...
    编程 发布于2024-11-16
  • Go 如何处理方法中的指针和值接收者?
    Go 如何处理方法中的指针和值接收者?
    Go 指针:接收者和值类型在 Go 中,指针对于理解面向对象编程和内存管理是必不可少的。在处理指针时,掌握方法中接收器类型之间的区别至关重要。您提供的 Go Tour 示例说明了这个概念:type Vertex struct { X, Y float64 } func (v *Vertex)...
    编程 发布于2024-11-16
  • 如何从 Python 中的字符串列表创建多个变量?
    如何从 Python 中的字符串列表创建多个变量?
    如何从字符串列表创建多个变量? [重复]许多编程场景要求我们同时操作多个对象或变量。一个常见的挑战是从字符串列表创建多个变量,其中每个变量的名称与列表中的相应元素匹配。在 Python 中,您可以使用字典理解来完成此操作:names = ['apple', 'orange', 'banana'] f...
    编程 发布于2024-11-16
  • 如何在 PHP 中组合两个关联数组,同时保留唯一 ID 并处理重复名称?
    如何在 PHP 中组合两个关联数组,同时保留唯一 ID 并处理重复名称?
    在 PHP 中组合关联数组在 PHP 中,将两个关联数组组合成一个数组是一项常见任务。考虑以下请求:问题描述:提供的代码定义了两个关联数组,$array1和$array2。目标是创建一个新数组 $array3,它合并两个数组中的所有键值对。 此外,提供的数组具有唯一的 ID,而名称可能重合。要求是构...
    编程 发布于2024-11-16
  • 在 Go 中使用 WebSocket 进行实时通信
    在 Go 中使用 WebSocket 进行实时通信
    构建需要实时更新的应用程序(例如聊天应用程序、实时通知或协作工具)需要一种比传统 HTTP 更快、更具交互性的通信方法。这就是 WebSockets 发挥作用的地方!今天,我们将探讨如何在 Go 中使用 WebSocket,以便您可以向应用程序添加实时功能。 在这篇文章中,我们将介绍: WebSoc...
    编程 发布于2024-11-16
  • 除了“if”语句之外:还有哪些地方可以在不进行强制转换的情况下使用具有显式“bool”转换的类型?
    除了“if”语句之外:还有哪些地方可以在不进行强制转换的情况下使用具有显式“bool”转换的类型?
    无需强制转换即可上下文转换为 bool您的类定义了对 bool 的显式转换,使您能够在条件语句中直接使用其实例“t”。然而,这种显式转换提出了一个问题:“t”在哪里可以在不进行强制转换的情况下用作 bool?上下文转换场景C 标准指定了四种值可以根据上下文转换为的主要场景bool:语句:if、whi...
    编程 发布于2024-11-16
  • 外键可以引用多态关联中的多个表吗?
    外键可以引用多态关联中的多个表吗?
    多态外键:一个外键可以引用多个表吗?关系数据库中外键的概念通常涉及指定确切的目标参考列应指向的表。然而,在处理多态关联时,其中一个表与一组中的多个其他表有关系,就会出现问题:是否可以有一个可以引用这些表中任何一个表的外键?答案:否在MySQL和PostgreSQL中,外键约束只能引用单个父表。此约束...
    编程 发布于2024-11-16
  • 如何以字符串数组的形式检索 TypeScript 接口的键?
    如何以字符串数组的形式检索 TypeScript 接口的键?
    以字符串数组的形式访问 Typescript 接口的键简介在 Typescript 中处理表格数据需要使用接口来定义列结构。为了有效地操作这些结构,通常需要以字符串数组的形式检索这些接口的属性名称。解决方案使用自定义转换器自 Typescript 版本 2.4 起,自定义转换器提供了一种从接口中提取...
    编程 发布于2024-11-16

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3