【spring源码系列-05】refresh中prepareRefresh方法的执行流程

Spring源码系列整体栏目


内容链接地址
【一】spring源码整体概述https://blog.csdn.net/zhenghuishengq/article/details/130940885
【二】通过refresh方法剖析IOC的整体流程https://blog.csdn.net/zhenghuishengq/article/details/131003428
【三】xml配置文件启动spring时refresh的前置工作https://blog.csdn.net/zhenghuishengq/article/details/131066637
【四】注解方式启动spring时refresh的前置工作https://blog.csdn.net/zhenghuishengq/article/details/131113249
【五】refresh中prepareRefresh的执行流程https://blog.csdn.net/zhenghuishengq/article/details/131186016

refresh中prepareRefresh方法的执行流程

  • 一,深度剖析refresh的prepareRefresh方法
    • 1,prepareRefresh()具体的执行流程
    • 2,initPropertySources(可扩展)
    • 3,总结

一,深度剖析refresh的prepareRefresh方法

前两篇谈到了refresh方法的前置工作和准备工作有哪些,注解的方式相对而言会比xml的方式需要做的前置工作更多。接下来就是进入最主要的refresh部分,前面几篇也粗略的对refresh里面的12个方法进行了粗略的概括,接下来的文章中,将对每一个方法做一个详细的概括。

再次查看这个refresh方法,首先会在这个方法上面加一个同步锁 synchronized ,用来保证线程的安全性

@Override
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {//1:准备刷新上下文环境prepareRefresh();//2:获取告诉子类初始化Bean工厂,不同工厂有不同的实现//  并且将配置文件的属性值加载到当前工厂中ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//3:对bean工厂进行填充属性prepareBeanFactory(beanFactory);try {// 第四:留个子类去实现该接口,bean工厂的后置处理器postProcessBeanFactory(beanFactory);// 调用我们的bean工厂的后置处理器. //1. 会在此将class扫描成beanDefinition  2.bean工厂的后置处理器调用invokeBeanFactoryPostProcessors(beanFactory);// 注册我们bean的后置处理器registerBeanPostProcessors(beanFactory);// 初始化国际化资源处理器.initMessageSource();// 创建事件多播器initApplicationEventMulticaster();// 这个方法同样也是留个子类实现的springboot也是从这个方法进行启动tomcat的.onRefresh();//把我们的事件监听器注册到多播器上registerListeners();// 实例化我们剩余的单实例bean.finishBeanFactoryInitialization(beanFactory);// 最后容器刷新 发布刷新事件(Spring cloud也是从这里启动的)finishRefresh();}}
}

1,prepareRefresh()具体的执行流程

接下来就是refresh中的第一个方法,也是本文的重点:prepareRefresh(),顾名思义,就是刷新前的准备工作,接下来进入这个方法中,查看内部详细的执行流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gB4ydeBy-1686631810191)(img/1686038023143.png)]

其主要代码片段如下

protected void prepareRefresh() {// Switch to active.this.startupDate = System.currentTimeMillis();this.closed.set(false);this.active.set(true);/*** 比如我们自己写一个类重写了initPropertySources方法,在该方法中设置了一个环境变量的值为A* 启动的时候,我的环境变量中没有该值就会启动抛出异常*/initPropertySources();/*** 用来校验我们容器启动必须依赖的环境变量的值*/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 < > ();
}

1,首先第一个是记录一下运行这个方法时的起始时间,最后通过方法结束获取的时间,来减去这个时间,就可以得到整个运行refresh方法的差值

this.startupDate = System.currentTimeMillis();   //容器启动的时间

2,随后就是设置两个标志位,一个是设置容器关闭的标志位,一个是设置容器激活的标志位

this.closed.set(false);   //容器关闭的标志位
this.active.set(true);    //容器激活的标志位

3,随后就是一个重要的方法 initPropertySources() ,该方法是一个空方法,主要是留给子类自己去实现,下面会专门举一个小demo来理解这个方法的作用。

protected void initPropertySources() {// For subclasses: do nothing by default.
}

4,随后一步就是先获取系统环境对象,在进入refresh方法之前就已经获取的 StandardEnvironment 实例对象,因此这里的环境对象不为空,直接将对象值返回。该对象内部包含了全部的系统变量和系统属性。

@Override
public ConfigurableEnvironment getEnvironment() {if (this.environment == null) {this.environment = createEnvironment();}return this.environment;
}

在拿到这个标准的环境对象之后,接下来就是通过这个validateRequiredProperties对里面的属性进行一个验证的操作

getEnvironment().validateRequiredProperties();

其对应的验证操作如下,会循环遍历这些属性,判断属性在系统变量中是否存在,如果不存在则抛出异常

@Override
public void validateRequiredProperties() {MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();for (String key: this.requiredProperties) {//判断当前属性是否存在set集合中,即是否为系统属性if (this.getProperty(key) == null) {//如果当前属性不是系统属性,则添加一个异常ex.addMissingRequiredProperty(key);}}//如果异常数量不为空,则抛出异常if (!ex.getMissingRequiredProperties().isEmpty()) {throw ex;}
}

在这个抛出的异常中,就打印了这么一句话

The following properties were declared as required but could not be resolved: 

5,随后一步就是创建一个事件监听对象,在spring启动的时候,这个早期的earlyApplicationListeners 监听器对象默认为空,但是在springboot中,由于在spring.factories中存在大量的事件需要先加载,因此在springBoot中该对象不为空,相当于是在spring的基础上的一个增强。所以需要对这个earlyApplicationListeners对象判断是都为空。

if (this.earlyApplicationListeners == null) {//spring使用,默认为空this.earlyApplicationListeners = new LinkedHashSet <> (this.applicationListeners);
} else {// Reset local application listeners to pre-refresh state.//springboot使用,先清除原有的,再添加this.applicationListeners.clear();this.applicationListeners.addAll(this.earlyApplicationListeners);
}

如在spring.factories的文件中,默认就会注入这么多的监听器。该功能主要是springboot的一个扩展机制

在这里插入图片描述

6,除了这个监听器在spring启动时为空之外,这个早期Event事件也为空。

this.earlyApplicationEvents = new LinkedHashSet<>();

2,initPropertySources(可扩展)

由于initPropertySources 这个方法内部是一个空方法,主要是留给子类去实现,因此在这里,可以定义一个类继承 ClassPathXmlApplicationContext 这个类,如下

/*** @author zhenghuisheng* @date : 2023/6/6*/
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {//String... locations:xml配置文件的路径public MyClassPathXmlApplicationContext(String... locations) {super(locations);}@Overrideprotected void initPropertySources() { / /初始化属性//设置属性getEnvironment().setRequiredProperties("USERNAME");//获取属性String requiredProperty = getEnvironment().getRequiredProperty("USERNAME");System.out.println("当前系统用户名称为:" + requiredProperty);//获取app名称String applicationName = getApplicationName();System.out.println("当前应用名称为:" + applicationName);//获取前置处理器List<BeanFactoryPostProcessor> list = getBeanFactoryPostProcessors();System.out.println("定义的bean工厂的后置处理器为的集合长度为:"+ list.size());...}
}

在上一篇中有写到,在refresh方法之前的前置工作中,就会获取所有的系统环境和系统变量,如下图就是系统环境变量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JQoMpucv-1686631810193)(img/1685947066781.png)]

因此这里可以直接拿到这些环境变量等。随后写一个主启动类进行测试

 public static void main(String[] args) {//扩展,用于鉴定属性ApplicationContext m = new MyClassPathXmlApplicationContext("classpath.a.xml");    
}

由于环境对象可以获取到,因此在这个环境变量里面设置一个属性

getEnvironment().setRequiredProperties("USERNAME");

上面设置的属性 USERNAME值,在设置之后会进行一个验证的操作,主要是验证该值在系统中是否存在,如果不存在则直接抛出异常。

void validateRequiredProperties() throws MissingRequiredPropertiesException;

开发中如果程序是需要包含某个值, 如需要某个属性或者某个前缀之类的,就可以在这一步进行判断,就不用进应用层里面去判断了。如在抽象类 AbstractEnvironment 中,有着这两个的具体实现,可以先设置值,随后会对设置的值进行验证。

//设置相关的属性值
@Override
public void setRequiredProperties(String...requiredProperties) {this.propertyResolver.setRequiredProperties(requiredProperties);
}
//验证属性值,属性在在系统属性中不存在,则立马抛出异常
@Override
public void validateRequiredProperties() throws MissingRequiredPropertiesException {this.propertyResolver.validateRequiredProperties();
}

验证的流程具体如下:判断当前系统属性有没有这个值,如果系统有这个值,则不管;没有这个值,则抛出异常报错

@Override
public void validateRequiredProperties() {MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();for (String key: this.requiredProperties) {//判断当前系统属性有没有这个值if (this.getProperty(key) == null) {ex.addMissingRequiredProperty(key);}}//如果系统有这个值,则不管;没有这个值,则抛出异常报错if (!ex.getMissingRequiredProperties().isEmpty()) {throw ex;}
}

主要作用就是提前做一个参数的校验,实际开发中用的也比较少。

3,总结

在这个prepareRefresh方法中,总结来说就是做了五件事情:设置容器启动时间、设置活跃状态为true、设置关闭状态为false、获取环境对象,并将当前系统的属性值设置到Environment对象中、实例化监听器对象和事件对象,并设置为空

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

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

相关文章

【嘉立创EDA】层次原理图功能使用,或放置复用图块方法

文章路标👉 文章解决问题主题内容小结文章解决问题 1️⃣ 在一些较为复杂的原理图绘制时,常需要使用到复用图块。何为复用图块,笔者摘取ST MB1136设计原理图纸可以较好表述这个功能,如下图所示: [本图摘取自NUCLEO-F103RB设计图纸 sheet 2 of 5] 图中左上角绿色图块为…

AI 绘画 - 建筑绘图辅助设计之 Controlnet SEG Depth

前情提要 2023-06-17 周六 杭州 雨转阴 小记: 周末的午休感觉还是没有尽兴&#xff0c;说是要乖乖休息却只是躺下闭目养神。 任务清单: a. Sketchup 安装和学习; b. Sketchup 建模学习; c. Controlnet SEG 语义分割基础&#xff1b; d. Controlnet Depth; 简介 应用实践 …

git 的基本操作

1. git建立本地仓库 在想要建立的目录下输入命令 git init 我们可以看一下 .git目录下有什么 2. 配置git本地仓库 配置用户的 name 和 email 命令&#xff1a;git config [...] 配置完后&#xff0c;我们像查看一下 刚才的配置 2.1 查看配置命令 git config -l 2.2 删除…

PyTorch的ONNX结合MNIST手写数字数据集的应用(.pth和.onnx的转换与onnx运行时)

在PyTorch以前的模型都是.pth格式&#xff0c;后面Meta跟微软一起做了一个.onnx的通用格式。这里对这两种格式文件&#xff0c;分别做一个介绍&#xff0c;依然使用MNIST数据集来做示例 1、CUDA下的pth文件 那pth文件里面是什么结构呢&#xff1f;其实在以前的文章就有介绍过…

HTML 超链接标签、图片标签

超链接标签 超链接描述 HTML使用标签<a>来设置超文本链接 超链接可以是一个字&#xff0c;一个词&#xff0c;或者一组词&#xff0c;也可以是一幅图像&#xff0c;您可以点击这些内容来跳转到新的文档或者当前文档中的某个部分。 <a href"url">链接文…

图片加载失败捕获上报及处理

图片加载失败捕获上报及处理 前端页面中加载最多的静态资源之一就是图片了&#xff0c;当出现图片加载失败时&#xff0c;非常影响用户体验。这时候我们就需要对图片是否成功加载进行判断&#xff0c;并对图片加载失败进行处理。 图片加载监听 单个捕获 HTML中的img标签可以…

微信小程序浏览docx,pdf等文件在线预览使用wx.openDocument

wx.downloadFile({ url: fileUrl,//pdf链接success(res) {wx.openDocument({ //打开文档filePath: res.tempFilePath,fileType: "pdf",//文档类型showMenu: true,success: function (res) {wx.showToast({title: 打开文档成功,})},fail: function (res) {wx.showToas…

springboot整合xxl-job

文章目录 前言一、xxl-job是什么&#xff1f;二、使用步骤1.下载源码,并部署好2.模仿xxl-job-executor-sample-springboot 自己建立一个服务1 引入xxl-job核心依赖2 创建服务,配置yml3 创建一个配置类,用于读取上述配置,并配置好handel信息4 创建一个执行器的任务类,用于执行真…

阵列模式综合第三部分:深度学习(附源码)

一、前言 这个例子展示了如何设计和训练卷积神经网络&#xff08;CNN&#xff09;来计算产生所需模式的元素权重。 二、介绍 模式合成是阵列处理中的一个重要课题。阵列权重有助于塑造传感器阵列的波束图案&#xff0c;以匹配所需图案。传统上&#xff0c;由于空间信号处理和频…

举例说明什么是卷积神经网络

卷积神经网络&#xff08;Convolutional Neural Network, CNN&#xff09;是一种深度学习模型&#xff0c;主要应用于计算机视觉任务&#xff0c;如图像分类、物体检测等。它通过卷积层、池化层和全连接层等组件来实现对图像的特征提取和分类。 现在我们以一个简单的图像分类任…

Android Compose UI实战练手----Google Bloom登录页

目录 1.概述2.页面展示1.1 亮色主题1.2暗色主题 3.登录页面拆分以及编码实现3.1 登录页面拆分3.2 编码实现3.2.1 LoginPage3.2.2 LoginTitle3.2.3 LoginInoutBox3.2.4 LoginHintWithUnderLine3.2.5 LoginButton 4.源码地址 1.概述 在之前的章节中我们已经介绍了如何实现Google…

什么是网络货运平台?

一、什么是网络货运平台&#xff1f; 网络货运平台是依托互联网平台整合配置运输资源&#xff0c;以承运人身份与托运人签订运输服务合同、承担承运人责任&#xff0c;委托实际承运人完成运输服务的物流平台。它通过互联网形式实现运输过程真实、公平、公正、合法&#xff0c…