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

目录

一、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.mfbz.cn/a/695180.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

31、matlab卷积运算:卷积运算、二维卷积、N维卷积

1、conv 卷积和多项式乘法 语法 语法1:w conv(u,v) 返回向量 u 和 v 的卷积。 语法2:w conv(u,v,shape) 返回如 shape 指定的卷积的分段。 参数 u,v — 输入向量 shape — 卷积的分段 full (默认) | same | valid full:全卷积 ‘same…

简记:为Docker配置服务代理

简记 为Docker配置服务代理 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite:http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/qq_28550263/art…

NLP实战入门——文本分类任务(TextRNN,TextCNN,TextRNN_Att,TextRCNN,FastText,DPCNN,BERT,ERNIE)

本文参考自https://github.com/649453932/Chinese-Text-Classification-Pytorch?tabreadme-ov-file,https://github.com/leerumor/nlp_tutorial?tabreadme-ov-file,https://zhuanlan.zhihu.com/p/73176084,是为了进行NLP的一些典型模型的总…

论文阅读——MIRNet

项目地址: GitHub - swz30/MIRNet: [ECCV 2020] Learning Enriched Features for Real Image Restoration and Enhancement. SOTA results for image denoising, super-resolution, and image enhancement.GitHub - soumik12345/MIRNet: Tensorflow implementation…

idea打开hierarchy面板

hierarchy:查看类层级关系图 不同版本的IDEA的快捷键不一样,同时如果修改了IDEA快捷键,也可能会不一样,具体查看可通过IDEA上方的Navigate来查看navigate--Type Hierarchy,就可以看见其快捷键了,我的快捷键…

简单记录玩4399游戏flash插件问题

一、因谷歌浏览器默认禁止flash插件自动运行,所以玩家在使用谷歌浏览器,访问www.4399.com平台页面或者4399小游戏(flash资源)时,可能会出现加载异常的情况。今天教大家如何开启flash插件 二、下载falsh官方插件 地址:Flash Player官方下载中心-Flash中国官网 三、如果您…

【IoT NTN】3GPP R18中关于各类IoT设备在NTN中的增强和扩展

博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持! 博主链接 本人就职于国际知名终端厂商,负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G技术研究。 博客内容主要围绕…

动态路由协议RIP(思科、华为)

#交换设备 动态路由协议RIP 路由协议 静态路由 需要管理员手动配置 动态路由 是在路由器上启用某动态路由协议,进行自己直连网段的宣告,从而相邻的路由器就可以学习的相邻的路由器所宣告的网段,每一台路由器都把自己直连的网段宣告出去&am…

LINUX网络FTP服务

一、FTP服务 FTP服务:file transfer protocol :文件传输协议。在网络上进行双向传输,也是一个应用程序。不同的操作系统有不同的FTP软件,但使用的协议是一样的。 FTP协议基于TCP协议,有两个端口,即20和21。 20端口&…

《大道平渊》· 拾壹 —— 商业一定是个故事:讲好故事,员工奋发,顾客买单。

《大道平渊》 拾壹 "大家都在喝,你喝不喝?" 商业一定是个故事,人民群众需要故事。 比如可口可乐的各种故事。 可口可乐公司也只是被营销大师们, 作为一种故事载体,发挥他们的本领。 营销大师们开发故事…

《精通ChatGPT:从入门到大师的Prompt指南》第11章:Prompt与AI的未来

第11章:Prompt与AI的未来 11.1 技术发展的新方向 在迅速发展的人工智能领域,Prompt工程作为与AI模型交互的核心方式,正处于技术创新的前沿。未来几年,Prompt工程将沿着多个新方向发展,这些方向不仅会改变我们与AI互动…

SOA的设计模式_2.企业服务总线模式

1.企业服务总线(|Enterprise Service Bus,ESB) 在企业基于SOA实施EAI、B2B和BMP的过程中,如果采用点对点的集成方式存在着复杂度高,可管理性差,复用度差和系统脆弱等问题。企业服务总线(…

(南京观海微电子)——温度对TFT影响及改善方式

温度如何损坏 LCD? 这个工作温度范围会影响设备内的电子部分,超出范围会导致 LCD 技术在高温下过热或在寒冷时变慢。 至于液晶层,如果放在高温下,它会变质,导致它和显示器本身出现缺陷。 LCD 温度限制: 什…

架构设计-加密解决的基本工具方法

软件工程实施过程中,经常会用到加密解密相关的工具类,整理如下: import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder;import javax.crypto.Cipher; import java.security.*; import java.security.interfaces.RSAPrivateKey; imp…

开发做前端好还是后端好?

目录 一、引言 二、两者的对比分析 技能要求和专业知识: 职责和工作内容: 项目类型和应用领域: 就业前景和市场需求: 三、技能转换和跨领域工作 评估当前技能: 确定目标领域: 掌握相关框架和库&a…

Vue3【十三】watch监视

Vue3【十三】watch监视 Vue3 中的watch祝你能监视以下四种数据 ref 定义的数据reactive定义的数据函数返回一个值一个包含上述内容的数组 案例截图 目录结构 案例代码 Person.vue <template><div class"person"><!-- <h1>Watch情况1&#xff…

Channels无法使用ASGI问题

Django Channels是一个基于Django的扩展, 用于处理WebSockets, 长轮询和触发器事件等实时应用程序. 它允许Django处理异步请求, 并提供了与其他WebSockets库集成的功能.当我们在Django Channels中使用ASGI_APPLICATION设置时, 我们可以指定一个新的ASGI应用程序来处理ASGI请求.…

Transformer动画讲解:Softmax函数

暑期实习基本结束了&#xff0c;校招即将开启。 不同以往的是&#xff0c;当前职场环境已不再是那个双向奔赴时代了。求职者在变多&#xff0c;HC 在变少&#xff0c;岗位要求还更高了。提前准备才是完全之策。 最近&#xff0c;我们又陆续整理了很多大厂的面试题&#xff0c…

数据结构之线性表(1)

数据结构之线性表 1.线性表的定义 线性表是一种线性结构。在一个线性表中数据元素的类型是相同的&#xff0c;或者说线性表是由同一类型的数据元素构成的线性结构。 线性表是具有相同数据类型的n&#xff08;n>0&#xff09;个数据元素的有限序列。 n表示表长&#xff0c;…

45.django - 开始建立第一个项目

1.django是什么&#xff1f; Django是一个高级的、免费的、开源的Web应用框架&#xff0c;它由Python编程语言编写而成。Django遵循模型-视图-控制器&#xff08;MVC&#xff09;的设计模式&#xff0c;但通常将其称为模型-视图-模板&#xff08;MVT&#xff09;架构。它的主要…