当前位置: 首页 > news >正文

Spring Boot 启动流程是怎么样的

引言

SpringBoot是一个广泛使用的Java框架,旨在简化基于Spring框架的应用程序的开发过程。在这篇文章中,我们将深入探讨SpringBoot应用程序的启动流程,了解其背后的机制。

Spring Boot 启动概览

SpringBoot应用程序的启动通常从一个包含 main 方法的类开始,例如:


@SpringBootApplication
public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class, args);}
}

这里的SpringApplication.run是启动SpringBoot应用程序的入口。接下来,我们将深入分析这个方法及其调用的各个阶段。

 SpringApplication 类详解

SpringApplication类是SpringBoot应用程序启动过程的核心类。它负责初始化应用程序的上下文,加载配置,启动嵌入式服务器等。

SpringApplication构造函数

SpringApplication类有多个构造函数,常用的是接收一个或多个Class<?> 参数的构造函数:


public SpringApplication(Class<?>... primarySources) {this.setInitializers((Collection) this.getSpringFactoriesInstances(ApplicationContextInitializer.class));this.setListeners((Collection) this.getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = this.deduceMainApplicationClass();
}

这里设置了初始化器和监听器,并推断出主应用程序类。

run 方法

SpringApplication.run方法是启动SpringBoot应用程序的入口:


public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return (new SpringApplication(primarySources)).run(args);
}

这个方法内部调用了SpringApplication实例的run方法:


public ConfigurableApplicationContext run(String... args) {// 初始化阶段this.configureHeadlessProperty();SpringApplicationRunListeners listeners = this.getRunListeners(args);listeners.starting();// 环境准备阶段ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);this.configureIgnoreBeanInfo(environment);// 上下文创建阶段Banner printedBanner = this.printBanner(environment);ConfigurableApplicationContext context = this.createApplicationContext();this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);// 上下文刷新阶段this.refreshContext(context);this.afterRefresh(context, applicationArguments);listeners.finished(context, null);// 应用程序运行阶段this.callRunners(context, applicationArguments);return context;
}

源码解析

准备阶段

在准备阶段中,Spring Boot 会加载应用程序的初始设置,并创建 Spring Boot 上下文。这个阶段的核心源码是 SpringApplication 类的 run() 方法,它会调用 Spring Boot 的各个初始化器进行初始化和准备工作。

public ConfigurableApplicationContext run(String... args) {// 启动计时器StopWatch stopWatch = new StopWatch();stopWatch.start();// 定义应用程序上下文和异常报告器列表ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();// 配置 Headless 属性configureHeadlessProperty();// 获取 Spring Boot 启动监听器SpringApplicationRunListeners listeners = getRunListeners(args);// 执行启动监听器的 starting 方法listeners.starting();try {// 解析命令行参数ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 准备应用程序环境ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);// 配置忽略 BeanInfoconfigureIgnoreBeanInfo(environment);// 打印 BannerBanner printedBanner = printBanner(environment);// 创建应用程序上下文context = createApplicationContext();// 获取异常报告器,关于异常报告,我下次专门讲一下SpringBoot 的异常收集器。exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);// 准备应用程序上下文prepareContext(context, environment, listeners, applicationArguments, printedBanner);// 刷新应用程序上下文refreshContext(context);// 刷新后操作afterRefresh(context, applicationArguments);// 停止计时器stopWatch.stop();// 记录启动日志if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}// 执行启动监听器的 started 方法listeners.started(context);// 执行 RunnercallRunners(context, applicationArguments);} catch (Throwable ex) {// 处理启动失败handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}try {// 执行启动监听器的 running 方法listeners.running(context);} catch (Throwable ex) {// 处理启动失败handleRunFailure(context, ex, exceptionReporters, null);throw new IllegalStateException(ex);}// 返回应用程序上下文return context;}

在 run() 方法中,Spring Boot 首先会创建一个 StopWatch 对象,用于记录整个启动过程的耗时。然后,Spring Boot 会调用 getRunListeners(args) 方法获取 Spring Boot 的各个启动监听器,并调用starting() 方法通知这些监听器启动过程已经开始。接着调用 prepareEnvironment(listeners, applicationArguments) 方法创建应用程序的环境变量。

这个方法会根据用户的配置和默认设置创建一个 ConfigurableEnvironment对象,并将其传给后面的 createApplicationContext() 方法。printBanner(environment) 方法打印启动界面的 Banner,调用 refreshContext(context)方法刷新上下文。这个方法会启动上下文,执行各种启动任务,包括创建 Web 服务器、加载应用程序的配置、初始化各种组件等。具体的启动任务会在刷新上下文阶段中进行。

应用上下文创建阶段

在应用上下文创建阶段中,Spring Boot 会创建应用程序的上下文,包括各种配置信息、Bean 的加载和初始化等。这个阶段的核心源码是 Spring Boot 自动配置机制,通过扫描 classpath 中的配置文件,自动加载和配置各种组件和 Bean。

protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {try {switch (this.webApplicationType) {case SERVLET:contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);break;case REACTIVE:contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);break;default:contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);}}catch (ClassNotFoundException ex) {throw new IllegalStateException("Unable to create a default ApplicationContext, " +"please specify an ApplicationContextClass", ex);}}return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

在 createApplicationContext() 方法中,Spring Boot 首先会判断应用程序的类型,如果是 Web 应用程序,则会创建一个 WebApplicationContext;否则,会创建一个普通的 ApplicationContext。调用 BeanUtils.instantiateClass(contextClass) 方法创建应用程序的上下文。这个方法会根据上面的逻辑创建一个相应的 ApplicationContext。调用 load() 方法加载应用程序的配置。

这个方法会扫描 classpath 中的各种配置文件,例如 application.propertiesapplication.ymlMETA-INF/spring.factories 等,自动配置各种组件和 Bean。调用 postProcessApplicationContext() 方法对应用程序的上下文进行后处理。这个方法会调用各种初始化器和监听器,执行各种初始化任务。

刷新上下文阶段

在刷新上下文阶段中,Spring Boot 会执行各种启动任务,包括创建 Web 服务器(刚才我们跟源码的时候也看到了,如上我的截图)、加载应用程序的配置、初始化各种组件等。这个阶段的核心源码是 Spring Boot 的刷新机制,它会调用各种初始化器和监听器,执行各种启动任务。


protected void refreshContext(ConfigurableApplicationContext applicationContext) {refresh(applicationContext);if (this.registerShutdownHook) {try {applicationContext.registerShutdownHook();}catch (AccessControlException ex) {// Not allowed in some environments.}}
}

在 refreshContext() 方法中调用 refresh(applicationContext) 方法刷新上下文。这个方法是 ApplicationContext 接口的核心方法,会启动上下文,执行各种启动任务。调用 registerShutdownHook() 方法注册应用程序的关闭钩子。这个方法会在应用程序关闭时自动执行,清理资源、关闭线程等,所以我们利用此特性在服务关闭的时候清理一些资源。并向外部发送告警通知。

在 refresh(applicationContext) 方法中,Spring Boot 会执行上下文的各种启动任务,包括创建 Web 服务器、加载应用程序的配置、初始化各种组件等。具体的启动任务会调用各种初始化器和监听器,例如:

for (ApplicationContextInitializer<?> initializer : getInitializers()) {initializer.initialize(applicationContext);
}

另外,Spring Boot 还会调用各种监听器,我们不做赘述,例如:

for (ApplicationListener<?> listener : getApplicationListeners()) {if (listener instanceof SmartApplicationListener) {SmartApplicationListener smartListener = (SmartApplicationListener) listener;if (smartListener.supportsEventType(eventType)&& smartListener.supportsSourceType(sourceType)) {invokeListener(smartListener, event);}}else if (supportsEvent(listener, eventType)) {invokeListener(listener, event);}
}

相关文章:

Spring Boot 启动流程是怎么样的

引言 SpringBoot是一个广泛使用的Java框架&#xff0c;旨在简化基于Spring框架的应用程序的开发过程。在这篇文章中&#xff0c;我们将深入探讨SpringBoot应用程序的启动流程&#xff0c;了解其背后的机制。 Spring Boot 启动概览 SpringBoot应用程序的启动通常从一个包含 m…...

【学习笔记】数据结构(三)

栈和队列 文章目录 栈和队列3.1 栈 - Stack3.1.1 抽象数据类型栈的定义3.1.2 栈的表示和实现 3.2 栈的应用举例3.2.1 数制转换3.2.2 括号匹配的检验3.2.3 迷宫求解3.2.4 表达式求值 - 波兰、逆波兰3.2.5 反转一个字符串或者反转一个链表 3.3 栈与递归的实现3.4 队列 - Queue3.4…...

学习python笔记:10,requests,enumerate,numpy.array

requests库&#xff0c;用于发送 HTTP 请求的 Python 库。 requests 是一个用于发送 HTTP 请求的 Python 库。它使得发送 HTTP 请求变得简单且人性化。以下是一些基本的 requests 函数及其用途&#xff1a; requests.get(url, **kwargs) 发送一个 GET 请求到指定的 URL。 i…...

经典神经网络(13)GPT-1、GPT-2原理及nanoGPT源码分析(GPT-2)

经典神经网络(13)GPT-1、GPT-2原理及nanoGPT源码分析(GPT-2) 2022 年 11 月&#xff0c;ChatGPT 成功面世&#xff0c;成为历史上用户增长最快的消费者应用。与 Google、FaceBook等公司不同&#xff0c;OpenAI 从初代模型 GPT-1 开始&#xff0c;始终贯彻只有解码器&#xff0…...

MySQL库与表的操作

目录 一、登录并进入数据库 1、登录 2、USE 命令 检查当前数据库 二、库的操作 1、创建数据库语法 2、举例演示 3、退出 三、字符集和校对规则 1、字符集&#xff08;Character Set&#xff09; 2、校对集&#xff08;Collation&#xff09; 总结 3、操作命令 …...

TTS 语音合成技术学习

TTS 语音合成技术 TTS&#xff08;Text-to-Speech&#xff0c;文字转语音&#xff09;技术是一种能够将文字内容转换为自然语音的技术。通过 TTS&#xff0c;机器可以“说话”&#xff0c;这大大增强了人与机器之间的互动能力。无论是在语音助手、导航系统还是电子书朗读器中&…...

小公司做自动化的困境

1. 人员数量不够 非常常见的场景, 开发没几个, 凭什么测试要那么多, 假设这里面有3个测试, 是不是得有1个人会搞框架? 是不是得有2人搞功能测试, 一个人又搞框架, 有些脚本, 真来得及吗? 2. 人员基础不够 现在有的大公司, 是这样子协作的, 也就是某模块需求谁谁测试的, 那么…...

基于pytorch框架的手写数字识别(保姆级教学)

1、前言 本文基于PyTorch框架,采用CNN卷积神经网络实现MNIST手写数字识别,不仅可以在GPU上,同时也可以在CPU上运行。方便即使只有CPU的小伙伴也可以运行该模型。本博客手把手教学,如何手写网络层(3层),以及模型训练,详细介绍各参数含义与用途。 2、模型源码解读 该模型…...

注意力机制在大语言模型中的应用

在大语言模型中&#xff0c;注意力机制&#xff08;Attention Mechanism&#xff09;用于捕获输入序列中不同标记&#xff08;token&#xff09;之间的关系和依赖性。这种机制可以动态地调整每个标记对当前处理任务的重要性&#xff0c;从而提高模型的性能。具体来说&#xff0…...

qt 实现对字体高亮处理原理

在Qt中实现对文本的字体高亮处理&#xff0c;通常涉及到使用QTextDocument、QTextCharFormat和QSyntaxHighlighter。下面是一个简单的例子&#xff0c;演示如何为一个文本编辑器&#xff08;假设是QTextEdit&#xff09;添加简单的关键词高亮功能&#xff1a; 步骤 1: 定义关键…...

SAP中通过财务科目确定分析功能来定位解决BILLING问题实例

接用户反馈&#xff0c;一笔销售订单做发货后做销售发票时&#xff0c;没有成功过账到财务&#xff0c;提示财户确定错误。 这个之前可以通过VF02中点击小绿旗来重新执行过财动作&#xff0c;看看有没有相应日志来定位问题。本次尝试用此方法&#xff0c;也没有找到相关线索。 …...

充电站,正在杀死加油站

最近&#xff0c;深圳公布了一组数据&#xff0c;深圳的超级充电站数量已超过传统加油站数量&#xff0c;充电枪数量也已超过加油枪数量。 从全国范围看&#xff0c;加油站关停的速度在加快。 充电站正在杀死加油站。 加油站&#xff0c;未来何去何从&#xff1f; 01. 减少 我…...

哪个牌子的超声波清洗机好?四样超卓超声波清洗机独具特色!

眼镜是许多人日常生活中必不可少的工具&#xff0c;然而&#xff0c;相信很多人都有过清洗眼镜的烦恼。传统的清洗眼镜的方法往往不够彻底&#xff0c;容易留下污渍或者划伤镜片。因此&#xff0c;超声波洗眼镜机成为了现代人清洗眼镜的新选择。超声波洗眼镜机通过利用超声波震…...

vue3中若v-model绑定的响应字段出现三级,该如何实现rules验证规则

比如以下内容&#xff1a; 配置的rules内容 const rulesref({title:[{required:true,message:"请输入标题",trigger:"blur"},{max:50,message:"最大不能超过256个字",trigger:"blur"}],Category:[{required:true,message:"请选择…...

Docker-Compose一键部署项目

Docker-Compose一键部署项目 目录 Docker-Compose一键部署项目介绍部署Django项目项目目录结构 docker-compose.ymlnginx的default.conf文件后端Dockerfile文件mysql.env一键部署DNS域名解析引起的跨域问题 介绍 Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的…...

【C++】相机标定源码笔记-线激光点云处理工具类

一个线激光点云处理工具类&#xff0c;它包含了一系列的方法用于处理和分析线激光扫描得到的点云数据。提供的功能包括&#xff1a; 通过文件或直接数据设置点云。计算线激光在机器人坐标系下的精度&#xff0c;输出内点的平均距离、最大距离、最小距离、总点数和内点数。提供了…...

解决Transformer根本缺陷,所有大模型都能获得巨大改进

即使最强大的 LLM 也难以通过 token 索引来关注句子等概念&#xff0c;现在有办法了。 最近两天&#xff0c;马斯克和 LeCun 的口水战妥妥成为大家的看点。这两位 AI 圈的名人你来我往&#xff0c;在推特&#xff08;现为 X&#xff09;上相互拆对方台。 LeCun 在宣传自家最新论…...

如何排查Java应用的死锁

排查Java应用中的死锁问题是一个复杂但重要的任务&#xff0c;因为死锁会导致应用程序停止响应&#xff0c;影响用户体验和系统稳定性。以下是一些方法和步骤&#xff0c;帮助你排查Java应用中的死锁。 1. 理解死锁的概念 在计算机科学中&#xff0c;死锁是指两个或多个线程相…...

JS面试题1

1. 延迟加载JS有哪些方式&#xff1f; defer: 等html全部解析完成&#xff0c;才会执行js代码&#xff0c;顺次执行js脚本 async&#xff1a;是和html解析同步的&#xff0c;不是顺次执行js脚本&#xff08;当有很多个js时&#xff09;&#xff0c;是谁先加载完谁先执行。 <…...

Linux网络 - 再谈、详谈UDP和TCP协议

文章目录 前言预备netstatpidofcat /etc/services 一、UDP协议UDP协议端格式UDP的缓冲区基于UDP的应用层协议 二、TCP协议1.TCP协议段格式确认应答(ACK)机制三次握手疑问1 最后一次客户端发给服务端的ACK请求怎么保证服务端能够收到&#xff1f; 四次挥手疑问2 为什么挥手是四次…...

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

css3笔记 (1) 自用

outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size&#xff1a;0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格&#xff…...

大数据学习(132)-HIve数据分析

​​​​&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

rnn判断string中第一次出现a的下标

# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

回溯算法学习

一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...