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

2023-09-20 22:55:38


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 Answer
    private final int years;

    // The Answer to Life, the Universe, and Everything
    private 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;
更多推荐

JVM——10.对象的内存布局

这篇文章,我们来了解一下对象在内存中的布局是什么样的。解释:前面有一篇文章我们讲了JVM中类的结构,那里讲的是一个java类,被编译成二进制字节码后,它的结构是什么样的,或者说按照jvm的标准,一个.class文件中类的结构是什么样的。而这里我们要讲的是根据类在堆中创建出来的对象,它在堆中是如何布局,即它在堆中的结构是

计算机操作系统 (王道考研)笔记(二)

重点知识点1内存1.1内存的基础知识1.1.1内存定义、作用1.1.2指令的工作原理1.1.3三种装入策略1.1.4从写程序到程序运行1.1.5链接的三种方式1.1.6总结1.2内存管理1.2.1内存空间的分配与回收a)连续分配管理b)非连续分配管理1)基本分页存储管理2)基本分段存储管理3)段页式存储管理1.2.2内

[LLM+AIGC] 01.应用篇之中文ChatGPT初探及利用ChatGPT润色论文对比浅析(文心一言 | 讯飞星火)

近年来,人工智能技术火热发展,尤其是OpenAI在2022年11月30日发布ChatGPT聊天机器人程序,其使用了Transformer神经网络架构(GPT-3.5),能够基于在预训练阶段所见的模式、统计规律和知识来生成回答,还能根据聊天的上下文进行互动,真正像人类一样来聊天交流以及完成复杂的NLP任务。基于此,为更好

Flutter粒子生成演示

演示:直接上代码:import'dart:math';import'dart:ui';import'package:flutter/material.dart';import'package:kq_flutter_widgets/widgets/chart/ex/extension.dart';classParticl

上传项目到github上

在github上先创建一个空仓库在github上新建一个仓库,点击你的头像,然后在出来的侧边栏选择Yourrepositories点击New创建一个新的仓库,即repository输入你的仓库名称,选择public或者private.尽量不要勾选README如果你的本地项目有readme文件的话,你在push的时候可能

ES修改字段的数据类型

--mysql修改字段数据类型语句ALTERTABLE`表名`MODIFYCOLUMN`列名`修改的字段类型;--hive修改字段数据类型语句ALTERTABLE表名CHANGECOLUMN列名修改的字段类型;--es修改字段数据类型语句无法通过一个语句进行修改。思路:1、对修改字段重新建修改类型的索引;2、将旧索引数

MyBatis 类型转换模块

文章目录前言TypeHandlerBaseTypeHandlerTypeHandler实现类TypeHandlerRegistryTypeAliasRegistryTypeHandler的应用SqlSessionFactory执行SQL语句前言MyBatis是一个持久层框架ORM框架,实现数据库中数据和Java对象中的

机器人中的数值优化(十六)—— 约束优化的应用:控制分配问题、碰撞距离计算、非线性MPC

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考,主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等,本系列文章篇数较多,不定期更新,上半部分介绍无约束优化,下半部分介绍带约束的优化,中间会穿插一些路径规划方面的应用实例二十五、控制分配问题控制分配对于冗余驱动的系统的含义

【IEEE会议】第五届机器人、智能控制与人工智能国际学术会议(RICAI 2023)

【IEEE列表会议】第五届机器人、智能控制与人工智能国际学术会议(RICAI2023)20235thInternationalConferenceonRobotics,IntelligentControlandArtificialIntelligence第五届机器人、智能控制与人工智能国际学术会议(RICAI2023)

Kindle电子书下载功能关闭怎么办,借助calibre和cpolar搭建私有的网络书库公网访问

Kindle中国电子书店停运不要慌,十分钟搭建自己的在线书库随时随地看小说!文章目录Kindle中国电子书店停运不要慌,十分钟搭建自己的在线书库随时随地看小说!1.网络书库软件下载安装2.网络书库服务器设置3.内网穿透工具设置4.公网使用kindle访问内网私人书库6月底,亚马逊公司旗下Kindle中国电子书店停止运营

c: Sorting Algorithms

SortAlgorithm.h/*****************************************************************//***\fileSortAlgorithm.h*\brief业务操作方法*VSCODEc11https://github.com/hustcc/JS-So

热文推荐