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

news2025/1/16 13:45:04

目录

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

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

三、初始化属性源

(一)GenericWebApplicationContext初始化属性源

(二)StaticWebApplicationContext初始化属性源

四、初始化早期事件集合


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

分析SpringBoot启动配置原理icon-default.png?t=N7T8https://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 {

    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // 准备上下文环境,包括初始化工厂、后置处理器等
            prepareRefresh();

            // 创建并初始化 BeanFactory
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // 设置 BeanFactory 的类加载器、资源加载器等
            prepareBeanFactory(beanFactory);

            try {
                // 允许子类对 BeanFactory 进行进一步的自定义处理
                postProcessBeanFactory(beanFactory);

                // 调用 BeanFactoryPostProcessors 进行后置处理
                invokeBeanFactoryPostProcessors(beanFactory);

                // 注册 BeanPostProcessors,用于对 Bean 实例进行后置处理
                registerBeanPostProcessors(beanFactory);

                // 初始化消息源
                initMessageSource();

                // 初始化事件广播器
                initApplicationEventMulticaster();

                // 初始化其他特殊 Bean
                onRefresh();

                // 注册关闭钩子
                registerListeners();

                // 初始化所有剩余的单例 Bean
                finishBeanFactoryInitialization(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=N7T8https://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() 方法中会初始化早期事件集合,也就是上面的这个空的集合对象,主要用于后续能够添加早期事件。

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

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

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

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

相关文章

亚马逊冗余库存处理

在亚马逊放置90天以上的产品,又不在正常的动销,就要采取一定的措施了。清库存方式: 最直接的方式——降价促销(至少要降价百分之三十以上,库龄越久,降价越狠)参加官方的活动促销的话是需要符合…

在 Word 中,如何有效调整文字与下划线之间的距离

🍉 CSDN 叶庭云:https://yetingyun.blog.csdn.net/ 如果你在使用 Word 时,希望调整文字和下划线之间的距离,让它们看起来更加美观,可以按照以下步骤操作: 1. 在你想要加下划线的文字前后各加一个空格&…

Tdengine的时序数据库简介、单机部署、操作语句及java应用

Tdengine的时序数据库简介、单机部署、操作语句及java应用 本文介绍了Tdengine的功能特点、应用场景、超级表和子表等概念,讲述了Tdengine2.6.0.34的单机部署,并介绍了taos数据库的常见使用方法及特色窗口查询方法,最后介绍了在java中的应用。…

Harmony中的HAP、HAR、HSP区别

Harmony中的HAP、HAR、HSP区别 想要更加合理的开发一个企业级别的Harmony应用,那么就不得不提其中的HAP、HAR、HSP了。 前言 对于普通的用户来说,可能一个普通的应用就等于一个安装文件如安卓下的APK。但是对于Harmony应用开发工程师来讲,…

Python | Leetcode Python题解之第143题重排链表

题目: 题解: class Solution:def reorderList(self, head: ListNode) -> None:if not head:returnmid self.middleNode(head)l1 headl2 mid.nextmid.next Nonel2 self.reverseList(l2)self.mergeList(l1, l2)def middleNode(self, head: ListNo…

单田芳mp3百度网盘,单田芳评书下载百度云百度网盘

单老的评书还注重情感的表达。他善于运用声音、语气、语调等手段,将人物的情感刻画得淋漓尽致。无论是喜怒哀乐,他都能准确地把握人物的情感变化,并通过自己的表演将其传递给听众。这种情感的传递,使得听众能够更加深入地理解故事…

Springboot 开发之任务调度框架(一)Quartz 简介

一、引言 常见的定时任务框架有 Quartz、elastic-job、xxl-job等等,本文主要介绍 Spirng Boot 集成 Quartz 定时任务框架。 二、Quartz 简介 Quartz 是一个功能强大且灵活的开源作业调度库,广泛用于 Java 应用中。它允许开发者创建复杂的调度任务&…

Web--CSS基础

文章目录 定义方式选择器文本字体背景边框元素展示格式内边距与外边距盒子模型位置浮动flex布局响应式布局 定义方式 行内样式表 直接定义在style属性中&#xff0c;作用于当前标签 <img src "/imges/logo.jpg" alt "" style "width 400"…

react修改本地运行项目的端口

一、描述 如果你想让项目在你想要的端口打开的话&#xff0c;就需要进行设置 二、代码 设置一下pages.json文件就可以了&#xff0c;如下&#xff1a; 如果想打开项目不需要点击下面的链接地址&#xff0c;让他运行npm run dev之后自己直接打开到浏览器的话&#xff0c;在后…

智能楼宇的智慧心脏:ARMxy工业计算机在自动化控制中的应用

智能楼宇已成为现代化城市不可或缺的一部分。在这场数字化转型浪潮中&#xff0c;ARMxy工业计算机凭借其强大的处理能力、高度的系统兼容性和灵活的I/O配置&#xff0c;成为了推动楼宇自动化控制领域创新的重要力量。 某大型商业综合体项目&#xff0c;面临着传统HVAC系统效率低…

Android Studio历史版本

android studio的历史版本

OpenAI 宕机事件:GPT 停摆的影响与应对

引言 2024年6月4日&#xff0c;OpenAI 的 GPT 模型发生了一次全球性的宕机&#xff0c;持续时间长达8小时。此次宕机不仅影响了OpenAI自家的服务&#xff0c;还导致大量用户涌向竞争对手平台&#xff0c;如Claude和Gemini&#xff0c;结果也导致这些平台出现故障。这次事件的广…

conda 创建环境失败

conda create -n pylableimg python3.10在conda &#xff08;base&#xff09;环境下&#xff0c;创建新的环境&#xff0c;失败。 报错&#xff1a; LookupError: didn’t find info-scipy-1.11.3-py310h309d312_0 component in C:\Users\Jane.conda\pkgs\scipy-1.11.3-py310h…

VMware Workstation Pro的最新下载地址

前言 VMware被Broadcom收购后现在的下载方式也改变了&#xff0c;Workstation Pro 和 Fusion Pro 产品现在起将免费供个人用户使用下载方式 首先先把下载地址打开 https://support.broadcom.com/group/ecx/productdownloads?subfamilyVMwareWorkstationPro 打开链接&#xff…

人工智能和机器学习这两个概念有什么区别?

什么是人工智能&#xff1f; 先来说下人工智能&#xff0c;人工智能&#xff08;Artificial Intelligence&#xff09;&#xff0c;英文缩写为AI&#xff0c;通俗来讲就是用机器去做在过去只有人能做的事。 人工智能最早是由图灵提出的&#xff0c;在1950年&#xff0c;计算机…

jenkins插件之Jdepend

JDepend插件是一个为构建生成JDepend报告的插件。 安装插件 JDepend Dashboard -->> 系统管理 -->> 插件管理 -->> Available plugins 搜索 Jdepend, 点击安装构建步骤新增执行shell #执行pdepend if docker exec phpfpm82 /tmp/composer/vendor/bin/pdepe…

Python酷库之旅-开启库房之门

目录 一、库的定义 二、库的组成 三、库的分类 四、如何学好Python库&#xff1f; 五、注意事项 六、推荐阅读 1、Python筑基之旅 2、Python函数之旅 3、Python算法之旅 4、Python魔法之旅 5、 博客个人主页 一、库的定义 在Python中&#xff0c;库(Library)是一个封…

基于深度学习网络的USB摄像头实时视频采集与人脸检测matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 将摄像头对这播放视频的显示器&#xff0c;然后进行识别&#xff0c;识别结果如下&#xff1a; 本课题中&#xff0c;使用的USB摄像头为&#xff…

目前比较好用的LabVIEW架构及其选择

LabVIEW提供了多种架构供开发者选择&#xff0c;以满足不同类型项目的需求。选择合适的架构不仅可以提高开发效率&#xff0c;还能确保项目的稳定性和可维护性。本文将介绍几种常用的LabVIEW架构&#xff0c;并根据不同项目需求和个人习惯提供选择建议。 常用LabVIEW架构 1. …

18.1 HTTP服务器-极简服务器、请求与响应

1. 极简服务器 大道至简。使用Go语言构建世界上最简单的HTTP服务器&#xff0c;仅需四行代码。 标准库的net/http包提供了多种用于创建HTTP服务器的方法&#xff0c;其中包括&#xff1a; http.HandleFunc("/", rootHandler) 第一参数&#xff1a;访问的url 第二…