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(),顾名思义,就是刷新前的准备工作,接下来进入这个方法中,查看内部详细的执行流程

其主要代码片段如下

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方法之前的前置工作中,就会获取所有的系统环境和系统变量,如下图就是系统环境变量

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

 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对象中、实例化监听器对象和事件对象,并设置为空

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

  1. Ioc容器beanDefinition-Spring 源码系列(1)

    Ioc容器beanDefinition-Spring 源码系列(1) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器 ...

  2. Spring源码系列:依赖注入(二)createBean

    在Spring源码系列:依赖注入(一)(AbstractBeanFactory-getBean)最后说道getBean是依赖注入的起点,bean的创建都是通过createBean来完成具体的创建的.c ...

  3. Spring源码系列:BeanDefinition载入(下)

    在Spring源码系列:BeanDefinition载入(上)中已经大概捋了一下解析过程,本篇将记录一下bean的注册过程. bean的注册就是DefaultListableBeanFactory中r ...

  4. Spring源码系列(十二)Spring创建Bean的过程(二)

    1.写在前面 上篇博客主要Spring在创建Bean的时候,第一次调用的Bean的后置处理器的过程,同时笔者也打算将整个Spring创建的Bean的过程,通过这个系列,将Bean的创建过程给讲清楚,废 ...

  5. Spring源码系列- Spring Beans - 核心类的基本介绍

    Spring源码系列- Spring Beans - 核心类的基本介绍 读过上一篇文章的读者应该都能对Spring的体系结构有一个大致的了解,在结尾处,我也说过会从spring-beans包开始分析, ...

  6. 如何将spring源码作为导入eclipse中,变成一个普通的项目(git、github)

    引子: 怎么查看spring-framework的源码?是不是用压缩软件解压jar包,然后用编辑软件看?高端一点的,是在eclipse上面,按住Ctrl键跳转着看?这里我给大家介绍更加高端一点的方法. ...

  7. Spring源码系列-第1章-Spring源码纵览【持续更新中】

    文章目录 必读 第1章-Spring源码纵览 概述 简单的继承关系图 Spring框架整体流程 核心组件接口分析 Resource资源 方法 实现类 ResourceLoader资源加载器 方法 实现 ...

  8. Spring源码系列:BeanFactory的创建

    2019独角兽企业重金招聘Python工程师标准>>> Spring的Ioc容器其实就是一个bean的关系网,依赖于core,bean,context三个组件来构建的.在spring ...

  9. Spring源码系列(十三)——Spring源码编译及详细注解

    文章目录 1. 环境搭建 2. 代码编译 2.1 编译代码 2.1.1 build.gradle 2.1.1.1 第一处 2.1.1.2 第二处 2.1.2 gradle.properties 2.1 ...

最新文章

  1. pytorch nn.Embedding
  2. 刘涵 美国 西北大学 计算机,西北大学关于表彰2010-2011学年度学生先进集体-红帆.doc...
  3. 直播电商加速合规,引爆消费潜力
  4. 转:SparkConf 配置的用法
  5. qt程序使用多行linux命令,开发Qt应用程序的基本方法总结
  6. STM32工作笔记0024---什么是电流,什么是电压,什么是电阻,电阻的作用
  7. FLUSH TABLES WITH READ LOCK有多快
  8. python客户端与服务器端通信_python客户端与服务器端的通信
  9. 添加css单词换行连字符
  10. 敏捷开发相关概念——学习笔记
  11. javaw java_我可以找出java程序是使用java还是javaw启动的
  12. 酒旅江湖战事:携程坚挺,美团蓄力,抖音来战
  13. jpg、png、jpeg区别与压缩等知识总结 —— 性能优化篇
  14. 用Asp.net 就能轻松实现铁道部的订票系统
  15. 《深入理解计算机系统》Lab2-Bomblab
  16. 0x0F1AFD76 (libcocos2d.dll) (Plane.exe 中)处有未经处理的异常: 0xC0000005: 读取位置 0x00000018 时发生访问冲突。
  17. 伪元素学习包含::before、::after的用法
  18. CUMT-CTF第二次双月赛Writeup
  19. String#intern
  20. 密码学入门(3):分组密码的模式

热门文章

  1. 南大计算机系为啥不升格为学院,浙江科技学院更名失败了吗?升格成大学没有...
  2. 转载:2008年软件业四大亮点回顾
  3. 听觉神经网络(一):听觉系统的结构与功能
  4. 2019年9月15日
  5. Oracle date数据类型的字段截取年月日
  6. c语言 命令行走迷宫小游戏
  7. 基于微信小程序的小型企业人力资源管理小程序
  8. python:输入圆半径计算圆周长、圆面积、圆球表面积(高教社,《Python编程基础及应用》习题3-10)
  9. matlab实现五子棋,matlab编程(五子棋
  10. mysql 或者条件_mysql条件查询and or使用实例及优先级介绍