spring framework 5.2文档 - 控制反转 IoC 容器

IoC 主题

  • 1.容器概述
  • 2. bean 概述
  • 3.依赖注入 (DI)
  • 4.Bean 的范围
  • 5.定制一个bean

Spring 框架最重要的是控制反转 (IoC) 容器

1.容器概述

org.springframework.context.ApplicationContext 接口代表 Spring IoC 容器,负责实例化、配置和组装 bean。 容器通过读取配置元数据来获取要实例化、配置和组装哪些对象的指令。 配置元数据以 XML、Java 注释或 Java 代码表示。 它可以让您表达组成应用程序的对象以及这些对象之间丰富的相互依赖性。

Spring 提供了 ApplicationContext 接口的多个实现。 在独立应用程序中,通常创建 ClassPathXmlApplicationContextFileSystemXmlApplicationContext 的实例。 虽然 XML 是定义配置元数据的传统格式,但您可以通过提供少量 XML 配置来指示容器使用 Java 注释或代码作为元数据格式,以声明方式启用对这些附加元数据格式的支持。

在大多数应用场景中,不需要显式的用户代码来实例化一个或多个 Spring IoC 容器实例。 例如,在 Web 应用程序场景中,应用程序的 web.xml 文件中的简单八行(左右)样板 Web 描述符 XML 通常就足够了(请参阅 Web 应用程序的便捷 ApplicationContext 实例化)。

下图显示了 Spring 工作原理的高级视图。 您的应用程序类与配置元数据相结合,以便在创建并初始化 ApplicationContext 后,您拥有一个完全配置且可执行的系统或应用程序。

2. bean 概述

Spring IoC 容器管理一个或多个 bean。 这些 bean 是使用您提供给容器的配置元数据创建的(例如,以 XML 定义的形式)。

在容器本身内,这些 bean 定义表示为 BeanDefinition 对象,其中包含(以及其他信息)以下元数据:

  • 包限定的类名:通常是所定义的 bean 的实际实现类。

  • Bean 行为配置元素,说明 Bean 在容器中的行为方式(范围、生命周期回调等)。

  • 对 Bean 完成其工作所需的其他 Bean 的引用。 这些引用也称为协作者或依赖项。

  • 在新创建的对象中设置的其他配置设置——例如,池的大小限制或管理连接池的 bean 中使用的连接数。

此元数据转换为构成每个 bean 定义的一组属性。 下表描述了这些属性:

属性说明
ClassInstantiating Beans
NameNaming Beans
ScopeBean Scopes
Constructor argumentsDependency Injection
PropertiesDependency Injection
Autowiring modeAutowiring Collaborators
Lazy initialization modeLazy-initialized Beans
Initialization methodInitialization Callbacks
Destruction methodDestruction Callbacks

NOTE:

ApplicationContext 实现还允许注册在容器外部(由用户)创建的现有对象。 这是通过 getBeanFactory() 方法访问 ApplicationContext 的 BeanFactory 来完成的,该方法返回 BeanFactory DefaultListableBeanFactory 实现。 DefaultListableBeanFactory 通过 registerSingleton(…) 和 registerBeanDefinition(…) 方法支持这种注册。 然而,典型的应用程序仅使用通过常规 bean 定义元数据定义的 bean。

Bean 元数据和手动提供的单例实例需要尽早注册,以便容器在自动装配和其他自省步骤期间正确推理它们。 虽然在某种程度上支持覆盖现有元数据和现有单例实例,但官方不支持在运行时注册新 bean(与对工厂的实时访问同时进行),并且可能会导致并发访问异常、bean 容器中的状态不一致。

3.依赖注入 (DI)

依赖注入 (DI) 是一个过程,对象仅通过构造函数参数、工厂方法的参数或对象实例构造后设置的属性来定义其依赖项(即与它们一起工作的其他对象)。 从工厂方法返回。 然后,容器在创建 bean 时注入这些依赖项。 这个过程从根本上来说是 bean 本身的逆过程(因此得名“控制反转”),通过使用类的直接构造或服务定位器模式自行控制其依赖项的实例化或位置。

采用 DI 原则,代码更加清晰,并且当对象提供其依赖项时,解耦更加有效。 该对象不会查找其依赖项,也不知道依赖项的位置或类。 因此,您的类变得更容易测试,特别是当依赖项位于接口或抽象基类上时,这允许在单元测试中使用存根或模拟实现。

DI 存在两种主要变体:基于构造函数的依赖注入和基于 Setter 的依赖注入。

基于构造函数的依赖注入

基于构造函数的 DI 是通过容器调用带有多个参数的构造函数来完成的,每个参数代表一个依赖项。 使用特定参数调用静态工厂方法来构造 bean 几乎是等效的,并且本讨论以类似方式对待构造函数和静态工厂方法的参数。 以下示例显示了一个只能通过构造函数注入进行依赖注入的类:

构造函数参数解析

构造函数参数解析匹配通过使用参数的类型进行。 如果 bean 定义的构造函数参数中不存在潜在的歧义,则在 bean 定义中定义构造函数参数的顺序就是在实例化 bean 时将这些参数提供给适当的构造函数的顺序。 考虑下面的类:

public class ExampleBean {// Number of years to calculate the Ultimate Answerprivate final int years;// The Answer to Life, the Universe, and Everythingprivate final String ultimateAnswer;public ExampleBean(int years, String ultimateAnswer) {this.years = years;this.ultimateAnswer = ultimateAnswer;}
}

依赖解析过程

容器执行bean依赖解析如下:

ApplicationContext 使用描述所有 bean 的配置元数据创建和初始化。 配置元数据可以通过 XML、Java 代码或注释来指定。

对于每个 bean,其依赖项以属性、构造函数参数或静态工厂方法的参数(如果您使用它而不是普通构造函数)的形式表示。 这些依赖关系是在实际创建 bean 时提供给 bean 的。

每个属性或构造函数参数都是要设置的值的实际定义,或对容器中另一个 bean 的引用。

作为值的每个属性或构造函数参数都会从其指定格式转换为该属性或构造函数参数的实际类型。 默认情况下,Spring 可以将以字符串格式提供的值转换为所有内置类型,例如 int、long、String、boolean 等。

Spring 容器在创建容器时验证每个 bean 的配置。 但是,直到实际创建 bean 后,才会设置 bean 属性本身。 单例范围并设置为预实例化(默认)的 Bean 是在创建容器时创建的。 范围在 Bean 范围中定义。 否则,仅当请求时才创建 bean。 创建 Bean 可能会导致创建 Bean 图表,因为创建并分配了 Bean 的依赖项及其依赖项的依赖项(等等)。 请注意,解析

循环依赖

如果主要使用构造函数注入,则可能会创建无法解析的循环依赖场景。

例如:A类通过构造函数注入需要B类的实例,B类通过构造函数注入需要A类的实例。 如果您为类 A 和 B 配置 Bean 以相互注入,Spring IoC 容器会在运行时检测到此循环引用,并抛出 BeanCurrentlyInCreationException

一种可能的解决方案是编辑某些类的源代码,使其由 setter 而不是构造函数进行配置。 或者,避免构造函数注入并仅使用 setter 注入。 换句话说,虽然不推荐,但是可以通过setter注入来配置循环依赖。

与典型情况(没有循环依赖)不同,bean A 和 bean B 之间的循环依赖会强制其中一个 Bean 在完全初始化之前注入另一个 Bean(典型的先有鸡还是先有蛋的场景)。

4.Bean 的范围

创建 bean 定义时,您将创建一个配方来创建由该 bean 定义定义的类的实际实例。 bean 定义是一个配方的想法很重要,因为这意味着,与类一样,您可以从单个配方创建许多对象实例。

您不仅可以控制要插入从特定 bean 定义创建的对象中的各种依赖项和配置值,还可以控制从特定 bean 定义创建的对象的范围。 这种方法功能强大且灵活,因为您可以通过配置选择创建的对象的范围,而不必在 Java 类级别烘焙对象的范围。 Bean 可以定义为部署在多个范围之一中。 Spring 框架支持六个作用域,其中四个作用域仅在您使用 Web 感知的 ApplicationContext 时才可用。 您还可以创建自定义范围。

下表描述了支持的范围:

  • 单例
    (默认)将单个 bean 定义范围限定为每个 Spring IoC 容器的单个对象实例。

  • 原型
    将单个 bean 定义的范围限定为任意数量的对象实例。

  • 请求(request)
    将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期。 也就是说,每个 HTTP 请求都有自己的 Bean 实例,该实例是根据单个 Bean 定义创建的。 仅在 Web 感知的 Spring ApplicationContext 上下文中有效。

  • 会话 (session)
    将单个 bean 定义的范围限定为 HTTP 会话的生命周期。 仅在 Web 感知的 Spring ApplicationContext 上下文中有效。

  • 应用 (application)
    将单个 bean 定义的范围限定为 ServletContext 的生命周期。 仅在 Web 感知的 Spring ApplicationContext 上下文中有效。

  • 网络套接字
    将单个 bean 定义的范围限定为 WebSocket 的生命周期。 仅在 Web 感知的 Spring ApplicationContext 上下文中有效。

5.定制一个bean

  • 生命周期回调 Callbacks

  • ApplicationContextAwareBeanNameAware

要与容器对 bean 生命周期的管理进行交互,您可以实现 Spring 的 InitializingBeanDisposableBean 接口。 容器为前者调用 afterPropertiesSet() ,为后者调用 destroy() ,让 Bean 在初始化和销毁 Bean 时执行某些操作。

JSR-250 @PostConstruct@PreDestroy 注释通常被认为是在现代 Spring 应用程序中接收生命周期回调的最佳实践。 使用这些注释意味着您的 bean 不会耦合到 Spring 特定的接口。 有关详细信息,请参阅使用@PostConstruct 和@PreDestroy。

如果您不想使用 JSR-250 注释,但仍想消除耦合,请考虑 init-method 和 destroy-method bean 定义元数据。

在内部,Spring 框架使用 BeanPostProcessor 实现来处理它可以找到的任何回调接口并调用适当的方法。 如果您需要 Spring 默认情况下未提供的自定义功能或其他生命周期行为,您可以自己实现 BeanPostProcessor。 有关更多信息,请参阅容器扩展点。

除了初始化和销毁回调之外,Spring 管理的对象还可以实现 Lifecycle 接口,以便这些对象可以参与容器自身生命周期驱动的启动和关闭过程。

初始化回调

org.springframework.beans.factory.InitializingBean 接口允许 bean 在容器设置 bean 的所有必要属性后执行初始化工作。 InitializingBean 接口指定了一个方法:

		void afterPropertiesSet() throws Exception;

销毁回调

实现 org.springframework.beans.factory.DisposableBean 接口可以让 bean 在包含它的容器被销毁时获得回调。 DisposableBean 接口指定了一个方法:

		void destroy() throws Exception;

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

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

相关文章

10万单词例句表单词句子ACCESS\EXCEL数据库

原本我以为《3万5千英语句子英语例句大全ACCESS数据库》例句已经够多了,没想到今天遇到一个10万条英语单词例句的数据,非常适合与单词词典进行关联学习,例句多了单词的用法以及句子的掌握都更有效率。 截图下方有显示“共有记录数”&#xff…

让高分辨率的相机芯片输出低分辨率的图片对于像素级的值有什么影响?

很多图像传感器可以输出多个分辨率的图像,如果选择低分辨率格式的图像输出,对于图像本身会有什么影响呢? 传感器本身还是使用全部像素区域进行感光,但是在像素数据输出时会进行所谓的降采样(down-sampling&#xff09…

一台主机外接两台显示器

一台主机外接两台显示器 写在最前面双屏配置软件双屏跳转 写在最前面 在使用电脑时需要运行多个程序,时不时就要频繁的切换,很麻烦 但就能用双屏显示来解决这个问题,用一台主机控制,同时外接两台显示器并显示不同画面。 参考&a…

深度学习保姆级教学

文章目录 前言1.深度学习概论2.神经网络1.基础原理2.损失函数3.SoftMax4.前向传播5.反向传播1.反向传播介绍 6 卷积神经网络应用1.检测任务2.超分辨率重构3.医学检测4.无人驾驶5. 人脸识别 6.卷积网络和传统区别7.卷积神经网络1.卷积做了什么?2.节点网络1.Alexnet2.…

100G QSFP28 100km光模块最新解决方案

随着信息时代的到来,数据传输的速度和距离要求越来越高。目前,易天光通信发布了具有超低成本、可实现100G超长距离传输新方案——100G QSFP28 100km光模块,该方案是在100G ZR4 80km光模块上的全面升级。 一、产品概述 100G ZR4 100km是专为…

【探索Linux】—— 强大的命令行工具 P.9(进程地址空间)

阅读导航 前言一、内存空间分布二、什么是进程地址空间1. 概念2. 进程地址空间的组成 三、进程地址空间的设计原理1. 基本原理2. 虚拟地址空间 概念 大小和范围 作用 虚拟地址空间的优点 3. 页表 四、为什么要有地址空间五、总结温馨提示 前言 前面我们讲了C语言的基础知识&am…

【web开发】7、Django(2)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、部门列表二、部门管理(增删改)三、用户管理过渡到modelform组件四、modelform实例:靓号操作五、自定义分页组件六、datepick…

Pytest接口自动化测试实战演练

结合单元测试框架pytest数据驱动模型allure 目录 api: 存储测试接口 conftest.py :设置前置操作 目前前置操作:1、获取token并传入headers,2、获取命令行参数给到环境变量,指定运行环境 commmon:存储封装的公共方法 connect_mysq…

集成Activiti-Modeler流程设计器

集成Activiti-Modeler流程设计器 Activiti Modeler 是 Activiti 官方提供的一款在线流程设计的前端插件,可以方便流程设计与开发人员绘制流程图,保存流程模型,部署至流程定义等等。 1、材料准备 首先我们需要获取activiti-explorer.zip&…

zabbix监控平台部署(二)

目录 一、自定义监控 二、Nginx监控 三、监控mysql 四、钉钉告警 五、163邮箱报警 总结 zabbix5.0 一、自定义监控 zabbix-agent(147) agent端操作 vim /etc/zabbix/zabbix_agentd.conf 在配置未文件末尾添加 UserParametermemory_userd,free…

Python数据容器:dict(字典、映射)

1、什么是字典 Python中的字典是通过key找到对应的Value(相当于现实生活中通过“字”找到“该字的含义” 我们前面所学习过的列表、元组、字符串以及集合都不能够提供通过某个东西找到其关联的东西的相关功能,字典可以。 例如 这里有一份成绩单&#xf…

vue修改node_modules打补丁步骤和注意事项

当我们使用 npm 上的第三方依赖包,如果发现 bug 时,怎么办呢? 想想我们在使用第三方依赖包时如果遇到了bug,通常解决的方式都是绕过这个问题,使用其他方式解决,较为麻烦。或者给作者提个issue,然…

【Java 基础篇】Java后台线程和守护线程详解

在Java多线程编程中,有两种特殊类型的线程:后台线程(Daemon Thread)和守护线程(Daemon Thread)。这两种线程在一些特定的场景下非常有用,但也需要谨慎使用。本文将详细介绍后台线程和守护线程的…

根据实体类生成表生成语句

新接手一个项目,没有数据库文件,并且Entity实体类放在不同的文件路径中,写了个转换工具,如下: import cn.hutool.core.collection.CollUtil; import com.baomidou.mybatisplus.annotations.TableId; import io.swagge…

(JavaEE)(多线程案例)线程池 (简单介绍了工厂模式)(含经典面试题ThreadPoolExector构造方法)

线程诞生的意义,是因为进程的创建/销毁,太重了(比较慢),虽然和进程比,线程更快了,但是如果进一步提高线程创建销毁的频率,线程的开销就不能忽视了。 这时候我们就要找一些其他的办法…

Ansible之Playbook的任务控制

一)Ansible 任务控制基本介绍 这⾥主要来介绍PlayBook中的任务控制。 任务控制类似于编程语⾔中的if … 、for … 等逻辑控制语句。 这⾥我们给出⼀个实际场景应⽤案例去说明在PlayBook中,任务控制如何应⽤。 在下⾯的PlayBook中,我们创建了…

pnpm入门教程

一、概述 1、更小 使用 npm 时,依赖每次被不同的项目使用,都会重复安装一次。 而在使用 pnpm 时,依赖会被存储在内容可寻址的存储中。 2、更快 依赖解析。 仓库中没有的依赖都被识别并获取到仓库。目录结构计算。 node_modules 目录结构是…

什么是GPT磁盘?介绍GPT(GUID 分区表)磁盘及其优势!

GPT概述 GPT磁盘是什么意思?GPT是全局唯一标识符分区表(GUID Partition Table)的简称,它是硬盘分区表结构的一个标准模式。在我们深入了解GPT磁盘的特性之前须知,MBR磁盘的分区信息直接保存在主引导记录&#xff0…

【探索C语言中VS调试技巧】:提高效率和准确性

文章目录 前言1. 什么是bug?2. 调试是什么?有多重要?2.1 调试是什么?2.2 调试的基本步骤2.3 Debug和Release的介绍 3. Windows环境调试介绍3.1 调试环境的准备3.2 学会快捷键3.3 调试的时候查看程序当前信息3.3.1 查看临时变量的值…

【C++】动态内存管理 ③ ( C++ 对象的动态创建和释放 | new 运算符 为类对象 分配内存 | delete 运算符 释放对象内存 )

文章目录 一、C 对象的动态创建和释放1、C 语言 对象的动态创建和释放 的方式2、C 语言 对象的动态创建和释放 的方式 二、代码示例 - 对象的动态创建和释放 一、C 对象的动态创建和释放 使用 C 语言中的 malloc 函数 可以为 类对象 分配内存 ; 使用 free 函数可以释放上述分配…