探究IOC容器刷新环节初始化前的预处理

目录

一、IOC容器的刷新环节快速回顾

二、初始化前的预处理prepareRefresh源码分析

三、初始化属性源

(一)GenericWebApplicationContext初始化属性源

(二)StaticWebApplicationContext初始化属性源

四、初始化早期事件集合

五、总结


干货分享,感谢您的阅读!

在很早之前我们单独写过一篇文章《分析SpringBoot启动配置原理》,具体可见:

分析SpringBoot启动配置原理icon-default.png?t=O83Ahttps://blog.csdn.net/xiaofeng10330111/article/details/130903779?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171664383116800186545975%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=171664383116800186545975&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-21-130903779-null-null.nonecase&utm_term=Spring&spm=1018.2226.3001.4450其中IOC容器的刷新环节可当重点分析,值得在读源码时进行深入分析,我们会从多个方向上再次进行分析回顾和学习。

一、IOC容器的刷新环节快速回顾

我们将AbstractApplicationContext的refresh方法源码提取并进行重点代码标注说明如下:

public abstract class AbstractApplicationContext implements ApplicationContext {@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// 准备上下文环境,包括初始化工厂、后置处理器等prepareRefresh();// 创建并初始化 BeanFactoryConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// 设置 BeanFactory 的类加载器、资源加载器等prepareBeanFactory(beanFactory);try {// 允许子类对 BeanFactory 进行进一步的自定义处理postProcessBeanFactory(beanFactory);// 调用 BeanFactoryPostProcessors 进行后置处理invokeBeanFactoryPostProcessors(beanFactory);// 注册 BeanPostProcessors,用于对 Bean 实例进行后置处理registerBeanPostProcessors(beanFactory);// 初始化消息源initMessageSource();// 初始化事件广播器initApplicationEventMulticaster();// 初始化其他特殊 BeanonRefresh();// 注册关闭钩子registerListeners();// 初始化所有剩余的单例 BeanfinishBeanFactoryInitialization(beanFactory);// 完成上下文刷新finishRefresh();} catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// 销毁已创建的 Bean,关闭容器destroyBeans();// 重置容器刷新标志,允许再次刷新cancelRefresh(ex);// 把异常重新抛出,允许调用者处理throw ex;} finally {// 重置已注册的 JVM 关闭钩子resetCommonCaches();}}}
}

以上内容可多次翻看并理解,本文将关注初始化前的预处理prepareRefresh专项。

二、初始化前的预处理prepareRefresh源码分析

prepareRefresh() 方法的设计和实现体现了 Spring 容器在初始化之前做好了各种准备工作,以确保容器在刷新过程中能够顺利进行,并且应用程序能够正确地运行。我们直接展示源码如下:

protected void prepareRefresh() {this.startupDate = System.currentTimeMillis();this.closed.set(false);this.active.set(true);if (this.logger.isDebugEnabled()) {if (this.logger.isTraceEnabled()) {this.logger.trace("Refreshing " + this);} else {this.logger.debug("Refreshing " + this.getDisplayName());}}// 初始化属性源this.initPropertySources();// 验证必需的属性this.getEnvironment().validateRequiredProperties();// 复制早期应用程序监听器以供稍后使用if (this.earlyApplicationListeners == null) {this.earlyApplicationListeners = new LinkedHashSet(this.applicationListeners);} else {this.applicationListeners.clear();this.applicationListeners.addAll(this.earlyApplicationListeners);}// 初始化早期事件集合this.earlyApplicationEvents = new LinkedHashSet();
}

我们通过这个源码可见prepareRefresh() 方法的主要步骤,简要说明如下:

步骤说明
记录时间戳和设置状态标志记录容器的启动时间戳,并设置容器的状态标志,用于跟踪容器的状态。
日志记录根据日志级别记录容器的刷新过程,提供对容器启动过程的可视化和追踪。
属性源初始化和属性验证初始化属性源,以确保应用程序在后续的运行过程中能够正确地获取配置属性,并验证必需的属性是否已经设置。
处理早期应用程序监听器复制早期应用程序监听器以供稍后使用,以确保在容器刷新过程中能够保留早期监听器的设置。
初始化早期事件集合初始化一个早期事件集合,用于存储在容器刷新过程中产生的早期事件,以便后续处理。

针对其中的主要内容我们进行展开分析一下。

三、初始化属性源

直接展开源码分析:

 protected void initPropertySources() {}

无论是 AbstractRefreshableWebApplicationContextGenericWebApplicationContext 还是 StaticWebApplicationContext,它们都具有 initPropertySources() 方法的实现,用于初始化容器的属性源,确保容器能够正确地获取应用程序的配置信息,因此在创建这些应用程序上下文时都可能会调用这个方法。

(一)GenericWebApplicationContext初始化属性源

具体源码展示:

    protected void initPropertySources() {ConfigurableEnvironment env = this.getEnvironment();if (env instanceof ConfigurableWebEnvironment) {((ConfigurableWebEnvironment)env).initPropertySources(this.servletContext, (ServletConfig)null);}} ====================================================
package org.springframework.web.context;import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.lang.Nullable;public interface ConfigurableWebEnvironment extends ConfigurableEnvironment {void initPropertySources(@Nullable ServletContext var1, @Nullable ServletConfig var2);
}====================================================
package org.springframework.web.context.support;import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.jndi.JndiLocatorDelegate;
import org.springframework.jndi.JndiPropertySource;
import org.springframework.lang.Nullable;
import org.springframework.web.context.ConfigurableWebEnvironment;public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";public StandardServletEnvironment() {}protected void customizePropertySources(MutablePropertySources propertySources) {propertySources.addLast(new PropertySource.StubPropertySource("servletConfigInitParams"));propertySources.addLast(new PropertySource.StubPropertySource("servletContextInitParams"));if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {propertySources.addLast(new JndiPropertySource("jndiProperties"));}super.customizePropertySources(propertySources);}public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {WebApplicationContextUtils.initServletPropertySources(this.getPropertySources(), servletContext, servletConfig);}
}

可以了解到 GenericWebApplicationContext 在初始化属性源时,会通过调用环境对象的 initPropertySources() 方法来实现。而环境对象通常是 StandardServletEnvironment 类的实例,它提供了对 Servlet 环境的特定支持,例如添加 Servlet 相关的属性源。

也即是说当我们在 Spring MVC 中创建一个基于 Java 配置的 Web 应用程序时,通常会使用 GenericWebApplicationContext 来管理应用程序上下文。在初始化容器时,GenericWebApplicationContext 会调用 initPropertySources() 方法来初始化属性源,这些属性源可能包括 Servlet 上下文参数、Servlet 配置参数以及 JNDI 属性等。这样,我们就能够在应用程序中方便地获取这些配置信息,例如通过 @Value 注解或 Environment 对象。

提到Environment 对象,可以扩展延读到:

重看Spring聚焦Environment分析-CSDN博客文章浏览阅读2.6k次,点赞17次,收藏12次。Environment模块在 Spring 中主要负责管理应用程序的配置和环境(定义为一组 profile配置文件)相关的信息,每个 profile 对应一个特定的应用程序部署环境,比如开发、测试、生产等。在这些 profile 中,可以包含各种属性,比如数据库连接信息、服务器端口、日志级别等。而对应的属性在 Spring 中被表示为键值对,其中键是属性的名称,值是属性的取值。属性可以通过不同的方式进行配置,比如在属性文件中、通过系统属性、操作系统环境变量等。https://blog.csdn.net/xiaofeng10330111/article/details/138143106?spm=1001.2014.3001.5501icon-default.png?t=O83Ahttps://blog.csdn.net/xiaofeng10330111/article/details/138143106?spm=1001.2014.3001.5501在 Spring Boot 应用程序启动时会自动加载 application.properties文件到 Environment 中。Spring Boot 的自动配置功能会根据这些配置属性来自动配置应用程序的各种组件和功能。

注意,虽然 initPropertySources() 方法在容器初始化时会起到一定的作用,但是在 Spring Boot 应用程序中,读取 application.properties 文件的功能主要是由 Spring Boot 框架本身负责的,它会将这些配置属性加载到 Environment 中,供整个应用程序使用。

(二)StaticWebApplicationContext初始化属性源

具体源码展示:

    protected void initPropertySources() {WebApplicationContextUtils.initServletPropertySources(this.getEnvironment().getPropertySources(), this.servletContext, this.servletConfig);}=======================================================public static void initServletPropertySources(MutablePropertySources sources, @Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {Assert.notNull(sources, "'propertySources' must not be null");String name = "servletContextInitParams";if (servletContext != null && sources.contains(name) && sources.get(name) instanceof PropertySource.StubPropertySource) {sources.replace(name, new ServletContextPropertySource(name, servletContext));}name = "servletConfigInitParams";if (servletConfig != null && sources.contains(name) && sources.get(name) instanceof PropertySource.StubPropertySource) {sources.replace(name, new ServletConfigPropertySource(name, servletConfig));}}

可以了解到 StaticWebApplicationContext 在初始化属性源时,会调用 WebApplicationContextUtils.initServletPropertySources() 方法来初始化 Servlet 相关的属性源。这些属性源通常包括了 Servlet 上下文参数和 Servlet 配置参数,它们是通过 ServletContextPropertySourceServletConfigPropertySource 来表示的。功能和常规的基本一致。

四、初始化早期事件集合

在上面的源码中可以直观的看到有个早期事件集合的初始化:

// 初始化早期事件集合
this.earlyApplicationEvents = new LinkedHashSet();

这段代码的作用是创建一个空的 LinkedHashSet 对象并赋值给 earlyApplicationEvents 变量,从而初始化了早期事件集合。

我们知道Spring框架是事件驱动的,它提供了一套事件机制用于在应用程序中处理各种事件。在容器的生命周期中,可能会产生各种事件,例如容器初始化完成事件、Bean初始化事件等。

在容器刷新过程中,一些事件可能会在容器完全初始化之前就已经发生。这些早期事件通常是在容器初始化的早期阶段触发的,例如在BeanFactory被创建之后但是Bean的实例化尚未开始之前。为了能够捕获并处理这些早期事件,Spring使用一个早期事件集合来存储这些事件。

在容器准备刷新之前在 prepareRefresh() 方法中会初始化早期事件集合,也就是上面的这个空的集合对象,主要用于后续能够添加早期事件。

也就是说,在容器刷新的过程中,如果产生了早期事件,就会将这些事件添加到早期事件集合中。这样,在容器刷新完成后,就可以从早期事件集合中获取这些事件,并进行后续的处理,例如执行事件监听器或发布事件通知。

一旦容器刷新完成,就可以对早期事件集合中的事件进行后续处理。这可能包括执行事件监听器、发布事件通知、执行一些初始化操作等。

五、总结

本文深入探讨了Spring IOC容器的刷新过程,主要集中在AbstractApplicationContextrefresh()方法的源码分析及其关键环节。文本文首先回顾了IOC容器的刷新流程,强调了各个步骤的重要性,例如准备环境、初始化BeanFactory、注册监听器和处理事件等。通过对prepareRefresh()方法的分析,读者可以看到Spring在容器初始化之前所做的准备工作,包括时间戳记录、状态标志设置和属性源的初始化。这些操作确保了容器在刷新过程中能够顺利进行。

接着,本文详细阐述了属性源初始化的具体实现,特别是在GenericWebApplicationContextStaticWebApplicationContext中的initPropertySources()方法。这一部分强调了如何从Servlet环境中获取配置属性,为应用程序提供必要的配置信息。此外,本文还探讨了早期事件集合的初始化及其在事件驱动机制中的作用,指出这些早期事件如何在容器刷新过程中的关键时刻被捕获和处理。

最后,本文提到了一些关键方法的异常处理机制,如destroyBeans()cancelRefresh(),强调了Spring容器在面临异常时的健壮性。整体而言,本文系统地分析了Spring容器的启动过程,为开发者理解和应用Spring框架提供了重要参考。

总结而言,本文不仅提供了丰富的源码分析,还强调了在实际应用中的潜在问题和最佳实践,对希望深入理解Spring框架内部机制的读者来说,具有很高的实用价值和参考意义。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

25.UE5时间膨胀,慢动作,切换地图,刷BOSS

2-27 时间膨胀、慢动作、切换地图、刷BOSS_哔哩哔哩_bilibili 目录 1.刷新BOSS逻辑 2.时间膨胀实现慢动作 3.胜利画面,下一关 3.1胜利画面UI 3.2第一关、第二关游戏模式 3.3下一关按钮事件的绑定 1.刷新BOSS逻辑 实现当场上的怪物都死亡后,进行刷…

自己编写的前后端分离程序,解决跨域问题

跨域问题在前端解决很麻烦,既然前后端都是自己编写的,就直接在后端解决了。 1. 后端中 在controller文件中加上 CrossOrigin // 解决跨域问题,不加的话虽然数据能正常传输,但是前端页面会没有正常响应 2. 前端中 可以正常访问…

小米顾此失彼:汽车毛利大增,手机却跌至低谷

科技新知 原创作者丨依蔓 编辑丨蕨影 三年磨一剑的小米汽车毛利率大增,手机业务毛利率却出现下滑景象。 11月18日,小米集团发布 2024年第三季度财报,公司实现营收925.1亿元,同比增长30.5%,预估902.8亿元;…

E45.【C语言】练习:输入10个整数查找找并打印不相同的数字及个数

1.题目 输入10个整数查找找并打印不相同的数字及个数 输入示例 数组元素个数:10 20 50 30 10 60 90 70 30 10 20 输出示例 20 50 30 10 60 90 70 一共7个 2.初始代码 和E27.【C语言】练习:在一个整型数组中,只有一个数字出现一次,其他数…

播放器开发之ffmpeg 硬件解码方案

硬件编解码的概念 硬件编解码是⾮CPU通过烧写运⾏视频加速功能对⾼清视频流进⾏编解码,其中⾮CPU可包括GPU、FPGA或者 ASIC等独⽴硬件模块,把CPU⾼使⽤率的视频解码⼯作从CPU⾥分离出来,降低CPU的使⽤负荷,使得平台能 ⾼效且流畅…

DDD领域应用理论实践分析回顾

目录 一、DDD的重要性 (一)拥抱互联网黑话(抓痛点、谈愿景、搞方法论) (二)DDD真的重要吗? 二、领域驱动设计DDD在B端营销系统的实践 (一)设计落地步骤 &#xff0…

读懂top后显示内容

第一行:系统信息 top - 06:33:12 up 42 min, 1 user, load average: 0.04, 0.02, 0.00 06:33:12:当前时间。up 42 min:系统已经启动了 42 分钟。1 user:当前有 1 个用户登录。load average: 0.04, 0.02, 0.00:这三个…

2024年第十四届APMCM亚太杯数学建模A题B题C题思路+代码解析汇总

2024年第十四届亚太地区大学生数学建模竞赛 开赛后第一时间更新解题思路代码参考文章! 点击链接加入群聊【2024亚太杯数学建模竞赛助攻】:http://qm.qq.com/cgi-bin/qm/qr?_wv1027&ksIq03p_73AYtWuH-bNy6VGD2652Um6y2&authKeyZsYwQzbxX0BKPyH…

Tomcat 如何管理 Session

Tomcat 如何管理 Session 我们知道,Tomcat 中每一个 Context 容器对应一个 Web 应用,而 Web 应用之间的 Session 应该是独立的,因此 Session 的管理肯定是 Context 级的,也就是一个 Context 一定关联多个 Session。 Tomcat 中主…

【ArcGISPro】使用AI模型提取要素-提取车辆(目标识别)

示例数据下载 栅格数据从网上随便找一个带有车辆的栅格数据 f094a6b1e205cd4d30a2e0f816f0c6af.jpg (1200799) (588ku.com) 添加数据

SpringBoot3_Web开发

4. 内容协商 一套系统适配多端数据返回 移动端:返回JSON数据第三方:返回XMLIoT:返回自定义协议数据 1. 默认规则 1. SpringBoot 多端内容适配 基于请求头内容协商 【默认】 客户端向服务端发送请求,携带HTTP标准的 Accept 请求…

Mysql篇-语句执行计划详解(explain)

概述 使用 explain 输出 SELECT 语句执行的详细信息,包括以下信息: 表的加载顺序 sql 的查询类型 可能用到哪些索引,实际上用到哪些索引 读取的行数 Explain 执行计划包含字段信息如下:分别是 id、select_type、table、partit…

.net 7.0 解决“The keyword field is required”的问题

在 .net 3.1项目的时候,使用 keyword 做 API 接口的模糊匹配,能够传入keyword “” 进行整表查询。但当我在 .net 7.0 项目中这么使用的时候,传入 keyword 不为空时能够进行匹配,但是当我传入 keyword “” 的时候就报错 “The …

高效语言模型 Parler-TTS 上线,一键完成文本转语音

Parler-TTS 是一种轻量级的文本转语音 (TTS) 模型,可以生成具有给定说话者风格的高质量、自然语音,自由度及创新性非常高,并且可以通过 Prompt 控制说话者的性别、音色、语调以及所处的场景(室内、室外、马路上、音乐厅等&#xf…

网络安全与防范

1.重要性 随着互联网的发达,各种WEB应用也变得越来越复杂,满足了用户的各种需求,但是随之而来的就是各种网络安全的问题。了解常见的前端攻击形式和保护我们的网站不受攻击是我们每个优秀fronter必备的技能。 2.分类 XSS攻击CSRF攻击网络劫…

JavaWeb——Maven、web入门

1. maven maven是一款用于管理和构建Java项目的工具,它基于项目对象模型(POM—Project Object Model)的概念,通过一小段描述信息来管理项目的构建。 1.1. 作用 1.1.1. 依赖管理 方便快捷的管理项目依赖的资源(jar包…

【前端学习笔记】Javascript学习二(运算符、数组、函数)

一、运算符 运算符(operator)也被称为操作符,是用于实现赋值、比较和执行算数运算等功能的符号。 JavaScript中常用的运算符有: 算数运算符、递增和递减运算符、比较运算符、逻辑运算符、赋值运算符 算数运算符: 、-…

【开源免费】基于Vue和SpringBoot的智慧食堂系统(附论文)

本文项目编号 T 629 ,文末自助获取源码 \color{red}{T629,文末自助获取源码} T629,文末自助获取源码 随着Internet的发展,人们的日常生活已经离不开网络。未来人们的生活与工作将变得越来越数字化,网络化和电子化。网…

基因组之全局互作热图可视化

引言 PlotHiC 是一个专为 Hi-C 数据可视化分析而设计的 Python 包。Hi-C 技术是一种能够检测染色体三维结构的实验方法,它能揭示 DNA 在细胞核内的三维组织结构。为了更好地展示和解释这些复杂的数据,PlotHiC[1] 可以帮助用户方便地绘制Hi-C 数据的热图。…

道本科技智慧合同管理平台,采用数字化技术帮助企业建立全生命周期的合同管理模式。

作为专业的企业合同管理平台建设专家,我们拥有丰富的实施经验和专业技术团队,致力于帮助企业搭建高效、安全的合同管理系统。我们的解决方案涵盖合同起草、审批、存储、分析和报告等多个环节,能够满足不同企业的多样化需求。 选择我们&#…