【SpringBoot3.0源码】启动流程源码解析 •下

news2025/7/10 23:46:58

文章目录

    • 初始化DefaultBootstrapContext
    • 开启Headless模式
    • 获取监听器并启动
    • 封装命令行参数
    • 准备环境
    • 打印Banner
    • 创建上下文容器
    • 预初始化上下文容器
    • 刷新Spring容器
    • 打印启动时间
    • 发布事件
    • 执行特定的run方法

上一篇《【SpringBoot3.0源码】启动流程源码解析 • 上》,主要讲解了new SpringApplication()设置了一些初始化器和监听器,接下来我们讲解下run方法的调用。

步入run方法:

在这里插入图片描述

public ConfigurableApplicationContext run(String... args) {
	// 记录时间
	long startTime = System.nanoTime();
	// 利用BootstrapRegistryInitializer初始化DefaultBootstrapContext对象
	DefaultBootstrapContext bootstrapContext = createBootstrapContext();
	ConfigurableApplicationContext context = null;
	// 开启了Headless模式
	configureHeadlessProperty();
	// 获取监听器
	SpringApplicationRunListeners listeners = getRunListeners(args);
	// 发布ApplicationStartingEvent事件
	listeners.starting(bootstrapContext, this.mainApplicationClass);
	try {
		// 根据命令行参数,实例化一个ApplicationArguments
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		// 准备环境
		ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
		// 打印Banner
		Banner printedBanner = printBanner(environment);
		// 据webApplicationType创建不同的Spring上下文容器(有三种)
		context = createApplicationContext();
		context.setApplicationStartup(this.applicationStartup);
		// 预初始化spring上下文
		prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
		// 刷新Spring容器
		refreshContext(context);
		afterRefresh(context, applicationArguments);
		// 打印启动时间
		Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
		}
		// 发布ApplicationStartedEvent事件和AvailabilityChangeEvent事件
		listeners.started(context, timeTakenToStartup);
		// 获取Spring容器中的ApplicationRunner/CommandLineRunner类型的Bean,并执行run方法
		callRunners(context, applicationArguments);
	}
	catch (Throwable ex) {
		if (ex instanceof AbandonedRunException) {
			throw ex;
		}
		// 发布ApplicationFailedEvent事件
		handleRunFailure(context, ex, listeners);
		throw new IllegalStateException(ex);
	}
	try {
		if (context.isRunning()) {
			Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
			// 发布ApplicationReadyEvent事件和AvailabilityChangeEvent事件
			listeners.ready(context, timeTakenToReady);
		}
	}
	catch (Throwable ex) {
		if (ex instanceof AbandonedRunException) {
			throw ex;
		}
		// 发布ApplicationFailedEvent事件
		handleRunFailure(context, ex, null);
		throw new IllegalStateException(ex);
	}
	return context;
}

初始化DefaultBootstrapContext

步入createBootstrapContext方法:

在这里插入图片描述

开启Headless模式

Headless模式是在缺少显示屏、键盘或者鼠标的系统配置。

步入configureHeadlessProperty方法:

private void configureHeadlessProperty() {
	System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
			System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}

启用headless模式,需要使用setProperty方法去设置相应的系统属性。

System.setProperty(“java.awt.headless”,true)

如果想在一个相同的程序 中使用headless和传统环境,你可以使用下面的命令行来完成:

java -Djava.awt.headless=true

获取监听器并启动

SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);

获取SpringApplicationRunListeners,SpringBoot提供了一个EventPublishingRunListener,它实现了SpringApplicationRunListener接口
Spring会利用这个类,发布一个ApplicationContextInitializedEvent事件,可以通过定义ApplicationListener来监听这个事件。

步入getRunListeners方法:调用getSpringFactoriesInstances方法获取监听器,这个方法前面前面讲过,因为在前面已经put进m缓存中,所以这里可以根据参数获取value值。

最后返回一个SpringApplicationRunListener实例。

在这里插入图片描述
接下来回去调用:

listeners.starting(bootstrapContext, this.mainApplicationClass);

步入starting方法:

在这里插入图片描述
步入doWithListeners方法:

在这里插入图片描述
首先会调用listenerAction: (listener) -> listener.starting(bootstrapContext)

步入starting方法:

在这里插入图片描述

步入multicastInitialEvent方法:

在这里插入图片描述
步入refreshApplicationListeners方法:
这7个监听器是我们之前加载到的:
在这里插入图片描述
调用this.initialMulticaster::addApplicationListener方法:
显式删除代理的目标(如果已注册),以避免对同一侦听器进行双重调用。
add到applicationListeners的set集合中。
在这里插入图片描述
执行完返回:

在这里插入图片描述
步入multicastEvent方法:

在这里插入图片描述
步入invokeListener方法:

在这里插入图片描述
doInvokeListener

在这里插入图片描述
listener.onApplicationEvent(event);

在这里插入图片描述
onApplicationStartingEvent

在这里插入图片描述
beforeInitialize

在这里插入图片描述
最后回到这里:

在这里插入图片描述
在这里插入图片描述

封装命令行参数

在这里插入图片描述

命令行参数配置:
在这里插入图片描述

DefaultApplicationArguments构造方法:

public DefaultApplicationArguments(String... args) {
	Assert.notNull(args, "Args must not be null");
	this.source = new Source(args);
	this.args = args;
}

准备环境

读取环境变量(操作系统的环境变量/JVM的环境变量),读取配置文件信息(基于监听器,会利用EventPublishingRunListener发布一个ApplicationEnvironmentPreparedEvent事件,默认会有一个EnvironmentPostProcessorApplicationListener来处理这个事件,当然也可以通过自定义ApplicationListener来处理这个事件,当ApplicationListener接收到这个事件之后,就会解析application.properties、application.yml文件,并添加到Environment中。

在这里插入图片描述

步入prepareEnvironment方法:

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
		// 根据不同的web类型创建不同实现的Environment对象,读取:java环境变量和系统环境变量
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		// 将命令行参数读取环境变量中
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		// 将@PropertieSource的配置信息 放在第一位,它的优先级是最低的
		ConfigurationPropertySources.attach(environment);
		// 发布了ApplicationEnvironmentPreparedEvent 的监听器 读取了全局配置文件
		listeners.environmentPrepared(bootstrapContext, environment);
		DefaultPropertiesPropertySource.moveToEnd(environment);
		Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
				"Environment prefix cannot be set via properties.");
		// 将所有spring.main 开头的配置信息绑定到SpringApplication中
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
			environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
		}
		// 更新PropertySources配置
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

在这里插入图片描述

根据不同的web类型创建不同实现的Environment对象,读取java环境变量和系统环境变量

ConfigurableEnvironment environment = getOrCreateEnvironment();

在这里插入图片描述

// 将命令行参数读取环境变量中
configureEnvironment(environment, applicationArguments.getSourceArgs());

在这里插入图片描述

// 将@PropertieSource的配置信息 放在第一位,它的优先级是最低的
ConfigurationPropertySources.attach(environment);

在这里插入图片描述

// 发布了ApplicationEnvironmentPreparedEvent的监听器,读取了全局配置文件
listeners.environmentPrepared(bootstrapContext, environment);

最终调用onApplicationEnvironmentPreparedEvent方法:

在这里插入图片描述

步入postProcessEnvironment方法:

在这里插入图片描述
步入processAndApply方法:

在这里插入图片描述

步入applyToEnvironment方法:
在这里插入图片描述

// 将所有spring.main 开头的配置信息绑定到SpringApplication中
bindToSpringApplication(environment);

在这里插入图片描述

打印Banner

// 打印Banner
Banner printedBanner = printBanner(environment);

步入printBanner方法:

在这里插入图片描述
步入print方法:

在这里插入图片描述
步入getBanner方法:

在这里插入图片描述
步入getTextBanner方法:首先获取spring.banner.location的值,如果没有就默认在根路径下,输出banner.txt文件。

在这里插入图片描述

获取完banner后,会输出:

banner.printBanner(environment, sourceClass, out);

在这里插入图片描述

步入该方法,输出banner:

在这里插入图片描述

创建上下文容器

// 据webApplicationType创建不同的Spring上下文容器(有三种)
context = createApplicationContext();

在这里插入图片描述

预初始化上下文容器

prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

步入prepareContext方法:

首先获取所有ApplicationContextInitializer, 循环调用initialize方法。

获取beanFactory。

对bean进行check,如果有重复的bean就会抛出异常。

将启动类注册到Spring容器中。

在这里插入图片描述

刷新Spring容器

这里前几章文章重点讲的,包括bean的加载、实例化、初始化、aop、事务、tomcat启动。

可以移步专栏查看:

《Java系核心技术》

打印启动时间

Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
			}

发布事件

// 发布ApplicationStartedEvent事件和AvailabilityChangeEvent事件
listeners.started(context, timeTakenToStartup);

最终来到这个方法进行事件的发布:

在这里插入图片描述
最终进行发布:

在这里插入图片描述

执行特定的run方法

// 获取Spring容器中的ApplicationRunner/CommandLineRunner类型的Bean,并执行run方法
callRunners(context, applicationArguments);

ApplicationRunner和CommandLineRunner简介

在这里插入图片描述


后续就是发布ApplicationReadyEvent事件和AvailabilityChangeEvent事件,以及对异常的处理。

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

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

相关文章

机械革命z2黑苹果改造计划第二番-MacOS实用软件渗透工具

机械革命z2黑苹果改造计划第二番-实用软件 Mac实用工具 这是旧电脑改造计划的第二篇,就是安装一些常用软件和一些渗透测试工具,武装灵魂成为真正的生产力工具 首先推荐一个网站,www.mactools.app,这个软件里边有大多数常用的软…

Unity中画2D图表(2)——用XChart包绘制散点分布图 + 一条直线方程

散点图用于显示关系。 对于 【相关性】 ,散点图有助于显示两个变量之间线性关系的强度。 对于 【回归】 ,散点图常常会添加拟合线。 举例1:你可以展示【年降雨量】与【玉米亩产量】的关系 举例2:你也可以分析各个【节假日】与【大…

减轻供应商风险的3个有效策略

每个企业都面临供应商风险,即由第三方引起的事件的可能性,这些事件可能或将对其运营提出挑战。管理供应商风险意味着提前识别可能出现的问题并制定计划来管理或减轻这些事件甚至供应商关系。本文总结了3个减轻供应商风险的策略。 1、使你的供应商基础多样…

金三银四丨黑蛋老师带你剖析-漏洞岗

作者丨黑蛋病毒岗之前我们简单看了看二进制逆向岗位和漏洞岗,今天我们来看一看病毒岗位,就单纯看二进制病毒岗位和漏洞岗位,其所需要的基础知识是差不多的,在Windows平台上,无非就是汇编,C语言,…

Jetson NX2 装机过程

1.固态硬盘安装完成后,系统配置 df -h 查看硬盘使用情况 2.查看Jetson NX的IP地址,以下两个都行 ifconfig ip address show 3.Jetson NX2安装arm64的annaconda3,安装有问题报错illegal instruction,未解决。 4.VNC远程登录 …

nginx负载均衡下的webshell上传

目录 场景描述 环境的安装 1、先将docker环境搭建起来 2、测试tomcat是否可以访问 3、查看docker中nginx反向代理的负载均衡 4、查看docker中lbsnode1中的ant.jsp文件 5、通过中国蚁剑来连接ant.jsp文件 复现过程 存在的问题 问题一:由于nginx采用的反向代…

初识WebRTC(Web Real-Time Communication)网络实时通信

W3C提供的WebRTC API 英文API:http://www.w3.org/TR/webrtc/(个人建议看英文版) 中文API:http://www.iwebrtc.com/blog/webrtc1-0/(old版) 三个主要API: MediaStream:获取音视频…

C++——继承和多态常见的面试问题

目录1. 继承和多态常见的面试问题1.1 概念查考1.2 问答题1.什么是多态?2.什么是重载、重写(覆盖)、重定义(隐藏)?3.多态的实现原理?4.inline函数可以是虚函数吗?5.静态成员可以是虚函数吗?6.构造函数可以是虚函数吗&am…

国产低功耗Soc蓝牙语音遥控器芯片HS6621 指纹锁、体脂称等应用方案

随着物联网技术不断发展,家用电器往智能化方向持续迭代,使用红外遥控器这种传统的互动方式已经满足不了实际的使用需求,蓝牙语音遥控器作为人机交互新载体,逐渐取代传统红外遥控器成为家居设备的标配。 相比于传统红外遥控器&…

C语言(数组和指针存储字符串)

目录 一.数组和指针 1.字符串数组和字符数组区别 2.数组存储字符串常量 3.指针存储字符串 二.数组和指针的区别 1.常量和变量 2.修改成本 3.存储成本 一.数组和指针 1.字符串数组和字符数组区别 char ch[]"hello world",末尾存储着\0,就是字符…

RabbitMQ工作模式

目录1.Work queues工作队列模式1.1 模式说明1.2 代码1.3 测试1.4 小结2.订阅模式类型3.Publish/Subscribe发布与订阅模式3.1 模式说明3.2 代码3.3 测试3.4 小结4.Routing路由模式4.1 模式说明4.2 代码4.3 测试4.4 小结5.Topics通配符模式5.1 模式说明5.2 代码5.3 测试5.4 小结6…

使用crontab执行定时任务

本来这个东西是挺简单的,是我脑子一直没转过来弯,我就想看看有多少人跟我一样😏 crontab语法自己去菜鸟教程看看就知道了,没什么难度 需求:每分钟定时执行一个PHP文件或者一个PHP命令 这是需要执行的文件&#xff0…

《Redis实战篇》七、Redis消息队列

7.1 Redis消息队列-认识消息队列 什么是消息队列:字面意思就是存放消息的队列。最简单的消息队列模型包括3个角色: 消息队列:存储和管理消息,也被称为消息代理(Message Broker)生产者:发送消息…

JAVA锁相关的概念和分类

JAVA对象和对象头 java中object对象一般由对象头、示例数据、填充字节三部分组成其中填充字节是为了补全对象大小为8bit的倍数而存在,没太多功能性要求对象头包括mark word和class point class point存放的是指向堆中数据类型的指针mark word是存储了许多和当前对象…

Linux配置和使用Git

本文已收录至《Linux知识与编程》专栏! 作者:ARMCSKGT 演示环境:CentOS 7 ​ 目录 前言 正文 注册Giett构建仓库 注册giett 构建仓库 Linux配置Git 下载Git 配置Git用户名 配置Git账户邮箱 验证是否初始化成功 生成授权证书 获…

重新认识 React Hooks useContext

通常来说,React 数据的传递方式都是一层一层把资料 props 传到子层的 就算第二层(Function Component)、第三层(Button Group Compontn) 根本没有用到这个资料,但是为了传到最底层(button) ,每一层还是必须要传props // App.js const App = () => {const [dark, setDark…

vue3:加载本地图片等静态资源

背景 在我们用 vue2 webpack 的时候&#xff0c;加载图片资源是这样用的&#xff1a; <img :src"require(/assets/test.png)" />这样打包后就会触发 file-loader 打包图片资源&#xff0c;在 dist 文件夹中就可以看到这个图片&#xff08;如果图片较小会打包…

Vue3+vite4使用mockjs进行模拟开发遇到的坑

Vue3vite4使用mockjs进行模拟开发遇到的坑 最近没那么忙了&#xff0c;就想着自己写一个后台管理系统的小demo。刚好最近把Vue3的文档撸了一遍&#xff0c;正好可以顺便练习一下Vue3ts。 插件 1、mockjs是必不可少的。 2、vite-plugin-mock。由于现在创建Vue3项目默认都使用vit…

Django框架之配置信息

静态文件 项目中的CSS、图片、js都是静态文件。一般会将静态文件放到一个单独的目录中&#xff0c;以方便管理。在html页面中调用时&#xff0c;也需要指定静态文件的路径&#xff0c;Django中提供了一种解析的方式配置静态文件路径。静态文件可以放在项目根目录下&#xff0c…

2023年,pmp项目管理师证书有用么?难考吗?

一、这个证书含金量高么&#xff1f; 是高的&#xff0c;在项目管理或者管理领域中&#xff0c;知名度和含金量都是在前列的。看项目岗位要求&#xff1a;“PMP证书优先”&#xff0c;“”具备项目管理资质&#xff08;PMP&#xff09;优先“。 且在行业内几乎是共识了&#…