探究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.rhkb.cn/news/474813.html

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

相关文章

【Java SE】PreparedStatement

PreparedStatement 是一个接口,它继承自 Statement,用于预编译 SQL 语句。简单来说,PreparedStatement 是 JDBC 提供的一个对象,用于执行 SQL 语句。它的重要功能是帮助防止 SQL 注入攻击,并提高执行效率。 SQL 注入问…

集群聊天服务器(7)数据模块

目录 Mysql数据库代码封装头文件与源文件 Mysql数据库代码封装 业务层代码不要直接写数据库,因为业务层和数据层的代码逻辑也想完全区分开。万一不想存储mysql,想存redis的话,就要改动大量业务代码。解耦合就是改起来很方便。 首先需要安装m…

数造科技亮相第26届高交会并接受媒体采访,以数据智能赋能未来

11 月 14 日至 16 日,第二十六届中国国际高新技术成果交易会(简称“高交会”)在深圳成功举办。本届大会以“科技引领发展,产业融合聚变”为主题,汇聚了全球最新的科技成果,打造了一场科技界的盛大聚会。 在…

C#获取视频第一帧_腾讯云媒体处理获取视频第一帧

一、 使用步骤: 第一步、腾讯云开启万象 第二步、安装Tencent.QCloud.Cos.Sdk 包 第三步、修改 腾讯云配置 图片存储目录配置 第四步、执行获取图片并保存 二、封装代码 using System.Text; using System.Threading.Tasks;using COSXML.Model.CI; using COSXML.A…

概念解读|K8s/容器云/裸金属/云原生...这些都有什么区别?

随着容器技术的日渐成熟,不少企业用户都对应用系统开展了容器化改造。而在容器基础架构层面,很多运维人员都更熟悉虚拟化环境,对“容器圈”的各种概念容易混淆:容器就是 Kubernetes 吗?容器云又是什么?容器…

吴恩达深度学习笔记:序列模型(Sequence Models) 1.3-1.4

目录 第五门课 序列模型(Sequence Models)第一周 循环序列模型(Recurrent Neural Networks)1.3 循环神经网络模型(Recurrent Neural Network Model)1.4 通过时间的反向传播(Backpropagation through time) …

语义分割(semantic segmentation)

语义分割(semantic segmentation) 文章目录 语义分割(semantic segmentation)图像分割和实例分割代码实现 语义分割指将图片中的每个像素分类到对应的类别,语义区域的标注和预测是 像素级的,语义分割标注的像素级的边界框显然更加精细。应用&#xff1a…

C++ —— string类(上)

目录 string的介绍 string类功能的使用介绍 constructor —— 构造 介绍使用(1)(2)(4) :构造、拷贝构造、带参构造 介绍(3):拷贝string类对象的一部分字符…

Python小白学习教程从入门到入坑------第三十二课 生成器(语法进阶)

目录 一、生成器 generator 1.1 生成器表达式 1.1.1 表达式一 1.1.2 表达式二 二、可迭代对象、迭代器、生成器三者之间的关系 2.1 定义与特性 2.2 关系与区别 一、生成器 generator 在Python中,生成器(Generators)是一种用于迭代对象…

商业iOS端路由架构演进

背景 目前商业SDK中的点击事件,会根据不同的「事件类型」「业务类型」,去执行不同的路由跳转逻辑,然而不同的跳转事件内部又有着很复杂的跳转逻辑, 痛点 不同的跳转逻辑之间存在耦合 例如,在deeplink的跳转逻辑之中…

数据结构(单向链表——c语言实现)

链式存储的优缺点: 优点: 1、动态分配内存: 链式存储不需要在数据插入之前分配固定大小的数组或内存块,因此它更适合存储动态变化的数据 2、高效的插入和删除操作: 在链表中插入或删除元素只需要调整相邻节点的指…

【SQL】E-R模型(实体-联系模型)

目录 一、介绍 1、实体集 定义和性质 属性 E-R图表示 2. 联系集 定义和性质 属性 E-R图表示 一、介绍 实体-联系数据模型(E-R数据模型)被开发来方便数据库的设计,它是通过允许定义代表数据库全局逻辑结构的企业模式&#xf…

Pytest-Bdd-Playwright 系列教程(12):步骤参数 parsers参数解析

Pytest-Bdd-Playwright 系列教程(12):步骤参数 & parsers参数解析 前言一、什么是步骤参数?二、pytest-bdd 的步骤参数用法2.1 简单字符串解析2.2 自定义正则表达式解析2.3 参数类型转换 三、案例:基于 pytest-bdd…

vscode 快捷键生成代码

1. !Tab/回车键 便捷生成html初始结构代码(注意!是英文字符) 2. Alt B 快捷默认浏览器打开 3. Ctrl / 增加注释 4. 光标放到该行即可,直接ctrlC,ctrlv,即可在下面复制一行 5. 选中要修改的标签…

前端接入Paymax支付请求

材料指南 开发者平台 :配置开发必备信息(appid,商户号,公钥私钥),此处与请求参数appId、merchantNo有关。 PayerMax Apis:各支付接口信息,本文以收银台支付API为请求展开,请求url为orderAndPay,测试环境基…

Jmeter的后置处理器(二)

5--JSR223 PostProcessor 功能特点 自定义后处理逻辑:使用脚本语言编写自定义的后处理逻辑。支持多种脚本语言:支持 Groovy、JavaScript、BeanShell 等脚本语言。动态参数传递:将提取的数据存储为变量,供后续请求使用。灵活性高…

CSS遮罩:mask

CSS属性 mask 允许使用者通过遮罩或者裁切特定区域的图片的方式来隐藏一个元素的部分或者全部可见区域。 // 一般用位图图片做遮罩 mask: url(~/assets/images/mask.png); mask-size: 100% 100%;// 使用 SVG 图形中的形状来做遮罩 mask: url(~/assets/images/mask.svg#star);…

Zmap+python脚本+burp实现自动化Fuzzing测试

声明 学习视频来自 B 站UP主泷羽sec,如涉及侵权马上删除文章。 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负。 ✍🏻作者简介:致…

15. Python中的os.path模块/路径操作相关

这个专栏记录我学习/科研过程中遇到的一些小问题以及解决方案,一些问题可能比较蠢请见谅。自用,仅供参考。 ------------------------------------------------------------------------------------ Python中的os.path模块详解(包括一些常…

鸿蒙实战:页面跳转传参

文章目录 1. 实战概述2. 实现步骤2.1 创建鸿蒙项目2.2 编写首页代码2.3 新建第二个页面 3. 测试效果4. 实战总结 1. 实战概述 本次实战,学习如何在HarmonyOS应用中实现页面间参数传递。首先创建项目,编写首页代码,实现按钮跳转至第二个页面并…